github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/taskrunner/connect_native_hook_test.go (about)

     1  package taskrunner
     2  
     3  import (
     4  	"context"
     5  	"io/ioutil"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	consulapi "github.com/hashicorp/consul/api"
    10  	consultest "github.com/hashicorp/consul/sdk/testutil"
    11  	"github.com/hashicorp/nomad/ci"
    12  	"github.com/hashicorp/nomad/client/allocdir"
    13  	"github.com/hashicorp/nomad/client/allocrunner/interfaces"
    14  	"github.com/hashicorp/nomad/client/taskenv"
    15  	"github.com/hashicorp/nomad/client/testutil"
    16  	agentconsul "github.com/hashicorp/nomad/command/agent/consul"
    17  	"github.com/hashicorp/nomad/helper/pointer"
    18  	"github.com/hashicorp/nomad/helper/testlog"
    19  	"github.com/hashicorp/nomad/helper/uuid"
    20  	"github.com/hashicorp/nomad/nomad/mock"
    21  	"github.com/hashicorp/nomad/nomad/structs"
    22  	"github.com/hashicorp/nomad/nomad/structs/config"
    23  	"github.com/stretchr/testify/require"
    24  )
    25  
    26  func getTestConsul(t *testing.T) *consultest.TestServer {
    27  	testConsul, err := consultest.NewTestServerConfigT(t, func(c *consultest.TestServerConfig) {
    28  		c.Peering = nil  // fix for older versions of Consul (<1.13.0) that don't support peering
    29  		if !testing.Verbose() { // disable consul logging if -v not set
    30  			c.Stdout = ioutil.Discard
    31  			c.Stderr = ioutil.Discard
    32  		}
    33  	})
    34  	require.NoError(t, err, "failed to start test consul server")
    35  	return testConsul
    36  }
    37  
    38  func TestConnectNativeHook_Name(t *testing.T) {
    39  	ci.Parallel(t)
    40  	name := new(connectNativeHook).Name()
    41  	require.Equal(t, "connect_native", name)
    42  }
    43  
    44  func setupCertDirs(t *testing.T) (string, string) {
    45  	fd, err := ioutil.TempFile(t.TempDir(), "connect_native_testcert")
    46  	require.NoError(t, err)
    47  	_, err = fd.WriteString("ABCDEF")
    48  	require.NoError(t, err)
    49  	err = fd.Close()
    50  	require.NoError(t, err)
    51  
    52  	return fd.Name(), t.TempDir()
    53  }
    54  
    55  func TestConnectNativeHook_copyCertificate(t *testing.T) {
    56  	ci.Parallel(t)
    57  
    58  	f, d := setupCertDirs(t)
    59  
    60  	t.Run("no source", func(t *testing.T) {
    61  		err := new(connectNativeHook).copyCertificate("", d, "out.pem")
    62  		require.NoError(t, err)
    63  	})
    64  
    65  	t.Run("normal", func(t *testing.T) {
    66  		err := new(connectNativeHook).copyCertificate(f, d, "out.pem")
    67  		require.NoError(t, err)
    68  		b, err := ioutil.ReadFile(filepath.Join(d, "out.pem"))
    69  		require.NoError(t, err)
    70  		require.Equal(t, "ABCDEF", string(b))
    71  	})
    72  }
    73  
    74  func TestConnectNativeHook_copyCertificates(t *testing.T) {
    75  	ci.Parallel(t)
    76  
    77  	f, d := setupCertDirs(t)
    78  
    79  	t.Run("normal", func(t *testing.T) {
    80  		err := new(connectNativeHook).copyCertificates(consulTransportConfig{
    81  			CAFile:   f,
    82  			CertFile: f,
    83  			KeyFile:  f,
    84  		}, d)
    85  		require.NoError(t, err)
    86  		ls, err := ioutil.ReadDir(d)
    87  		require.NoError(t, err)
    88  		require.Equal(t, 3, len(ls))
    89  	})
    90  
    91  	t.Run("no source", func(t *testing.T) {
    92  		err := new(connectNativeHook).copyCertificates(consulTransportConfig{
    93  			CAFile:   "/does/not/exist.pem",
    94  			CertFile: "/does/not/exist.pem",
    95  			KeyFile:  "/does/not/exist.pem",
    96  		}, d)
    97  		require.EqualError(t, err, "failed to open consul TLS certificate: open /does/not/exist.pem: no such file or directory")
    98  	})
    99  }
   100  
   101  func TestConnectNativeHook_tlsEnv(t *testing.T) {
   102  	ci.Parallel(t)
   103  
   104  	// the hook config comes from client config
   105  	emptyHook := new(connectNativeHook)
   106  	fullHook := &connectNativeHook{
   107  		consulConfig: consulTransportConfig{
   108  			Auth:      "user:password",
   109  			SSL:       "true",
   110  			VerifySSL: "true",
   111  			CAFile:    "/not/real/ca.pem",
   112  			CertFile:  "/not/real/cert.pem",
   113  			KeyFile:   "/not/real/key.pem",
   114  		},
   115  	}
   116  
   117  	// existing config from task env stanza
   118  	taskEnv := map[string]string{
   119  		"CONSUL_CACERT":          "fakeCA.pem",
   120  		"CONSUL_CLIENT_CERT":     "fakeCert.pem",
   121  		"CONSUL_CLIENT_KEY":      "fakeKey.pem",
   122  		"CONSUL_HTTP_AUTH":       "foo:bar",
   123  		"CONSUL_HTTP_SSL":        "false",
   124  		"CONSUL_HTTP_SSL_VERIFY": "false",
   125  	}
   126  
   127  	t.Run("empty hook and empty task", func(t *testing.T) {
   128  		result := emptyHook.tlsEnv(nil)
   129  		require.Empty(t, result)
   130  	})
   131  
   132  	t.Run("empty hook and non-empty task", func(t *testing.T) {
   133  		result := emptyHook.tlsEnv(taskEnv)
   134  		require.Empty(t, result) // tlsEnv only overrides; task env is actually set elsewhere
   135  	})
   136  
   137  	t.Run("non-empty hook and empty task", func(t *testing.T) {
   138  		result := fullHook.tlsEnv(nil)
   139  		require.Equal(t, map[string]string{
   140  			// ca files are specifically copied into FS namespace
   141  			"CONSUL_CACERT":          "/secrets/consul_ca_file.pem",
   142  			"CONSUL_CLIENT_CERT":     "/secrets/consul_cert_file.pem",
   143  			"CONSUL_CLIENT_KEY":      "/secrets/consul_key_file.pem",
   144  			"CONSUL_HTTP_SSL":        "true",
   145  			"CONSUL_HTTP_SSL_VERIFY": "true",
   146  		}, result)
   147  	})
   148  
   149  	t.Run("non-empty hook and non-empty task", func(t *testing.T) {
   150  		result := fullHook.tlsEnv(taskEnv) // task env takes precedence, nothing gets set here
   151  		require.Empty(t, result)
   152  	})
   153  }
   154  
   155  func TestConnectNativeHook_bridgeEnv_bridge(t *testing.T) {
   156  	ci.Parallel(t)
   157  
   158  	t.Run("without tls", func(t *testing.T) {
   159  		hook := new(connectNativeHook)
   160  		hook.alloc = mock.ConnectNativeAlloc("bridge")
   161  
   162  		t.Run("consul address env not preconfigured", func(t *testing.T) {
   163  			result := hook.bridgeEnv(nil)
   164  			require.Equal(t, map[string]string{
   165  				"CONSUL_HTTP_ADDR": "unix:///alloc/tmp/consul_http.sock",
   166  			}, result)
   167  		})
   168  
   169  		t.Run("consul address env is preconfigured", func(t *testing.T) {
   170  			result := hook.bridgeEnv(map[string]string{
   171  				"CONSUL_HTTP_ADDR": "10.1.1.1",
   172  			})
   173  			require.Empty(t, result)
   174  		})
   175  	})
   176  
   177  	t.Run("with tls", func(t *testing.T) {
   178  		hook := new(connectNativeHook)
   179  		hook.alloc = mock.ConnectNativeAlloc("bridge")
   180  		hook.consulConfig.SSL = "true"
   181  
   182  		t.Run("consul tls server name not preconfigured", func(t *testing.T) {
   183  			result := hook.bridgeEnv(nil)
   184  			require.Equal(t, map[string]string{
   185  				"CONSUL_HTTP_ADDR":       "unix:///alloc/tmp/consul_http.sock",
   186  				"CONSUL_TLS_SERVER_NAME": "localhost",
   187  			}, result)
   188  		})
   189  
   190  		t.Run("consul tls server name preconfigured", func(t *testing.T) {
   191  			result := hook.bridgeEnv(map[string]string{
   192  				"CONSUL_HTTP_ADDR":       "10.1.1.1",
   193  				"CONSUL_TLS_SERVER_NAME": "consul.local",
   194  			})
   195  			require.Empty(t, result)
   196  		})
   197  	})
   198  }
   199  
   200  func TestConnectNativeHook_bridgeEnv_host(t *testing.T) {
   201  	ci.Parallel(t)
   202  
   203  	hook := new(connectNativeHook)
   204  	hook.alloc = mock.ConnectNativeAlloc("host")
   205  
   206  	t.Run("consul address env not preconfigured", func(t *testing.T) {
   207  		result := hook.bridgeEnv(nil)
   208  		require.Empty(t, result)
   209  	})
   210  
   211  	t.Run("consul address env is preconfigured", func(t *testing.T) {
   212  		result := hook.bridgeEnv(map[string]string{
   213  			"CONSUL_HTTP_ADDR": "10.1.1.1",
   214  		})
   215  		require.Empty(t, result)
   216  	})
   217  }
   218  
   219  func TestConnectNativeHook_hostEnv_host(t *testing.T) {
   220  	ci.Parallel(t)
   221  
   222  	hook := new(connectNativeHook)
   223  	hook.alloc = mock.ConnectNativeAlloc("host")
   224  	hook.consulConfig.HTTPAddr = "http://1.2.3.4:9999"
   225  
   226  	t.Run("consul address env not preconfigured", func(t *testing.T) {
   227  		result := hook.hostEnv(nil)
   228  		require.Equal(t, map[string]string{
   229  			"CONSUL_HTTP_ADDR": "http://1.2.3.4:9999",
   230  		}, result)
   231  	})
   232  
   233  	t.Run("consul address env is preconfigured", func(t *testing.T) {
   234  		result := hook.hostEnv(map[string]string{
   235  			"CONSUL_HTTP_ADDR": "10.1.1.1",
   236  		})
   237  		require.Empty(t, result)
   238  	})
   239  }
   240  
   241  func TestConnectNativeHook_hostEnv_bridge(t *testing.T) {
   242  	ci.Parallel(t)
   243  
   244  	hook := new(connectNativeHook)
   245  	hook.alloc = mock.ConnectNativeAlloc("bridge")
   246  	hook.consulConfig.HTTPAddr = "http://1.2.3.4:9999"
   247  
   248  	t.Run("consul address env not preconfigured", func(t *testing.T) {
   249  		result := hook.hostEnv(nil)
   250  		require.Empty(t, result)
   251  	})
   252  
   253  	t.Run("consul address env is preconfigured", func(t *testing.T) {
   254  		result := hook.hostEnv(map[string]string{
   255  			"CONSUL_HTTP_ADDR": "10.1.1.1",
   256  		})
   257  		require.Empty(t, result)
   258  	})
   259  }
   260  
   261  func TestTaskRunner_ConnectNativeHook_Noop(t *testing.T) {
   262  	ci.Parallel(t)
   263  	logger := testlog.HCLogger(t)
   264  
   265  	alloc := mock.Alloc()
   266  	task := alloc.Job.LookupTaskGroup(alloc.TaskGroup).Tasks[0]
   267  	allocDir, cleanup := allocdir.TestAllocDir(t, logger, "ConnectNative", alloc.ID)
   268  	defer cleanup()
   269  
   270  	// run the connect native hook. use invalid consul address as it should not get hit
   271  	h := newConnectNativeHook(newConnectNativeHookConfig(alloc, &config.ConsulConfig{
   272  		Addr: "http://127.0.0.2:1",
   273  	}, logger))
   274  
   275  	request := &interfaces.TaskPrestartRequest{
   276  		Task:    task,
   277  		TaskDir: allocDir.NewTaskDir(task.Name),
   278  	}
   279  	require.NoError(t, request.TaskDir.Build(false, nil))
   280  
   281  	response := new(interfaces.TaskPrestartResponse)
   282  
   283  	// Run the hook
   284  	require.NoError(t, h.Prestart(context.Background(), request, response))
   285  
   286  	// Assert the hook is Done
   287  	require.True(t, response.Done)
   288  
   289  	// Assert no environment variables configured to be set
   290  	require.Empty(t, response.Env)
   291  
   292  	// Assert secrets dir is empty (no TLS config set)
   293  	checkFilesInDir(t, request.TaskDir.SecretsDir,
   294  		nil,
   295  		[]string{sidsTokenFile, secretCAFilename, secretCertfileFilename, secretKeyfileFilename},
   296  	)
   297  }
   298  
   299  func TestTaskRunner_ConnectNativeHook_Ok(t *testing.T) {
   300  	ci.Parallel(t)
   301  	testutil.RequireConsul(t)
   302  
   303  	testConsul := getTestConsul(t)
   304  	defer testConsul.Stop()
   305  
   306  	alloc := mock.Alloc()
   307  	alloc.AllocatedResources.Shared.Networks = []*structs.NetworkResource{{Mode: "host", IP: "1.1.1.1"}}
   308  	tg := alloc.Job.TaskGroups[0]
   309  	tg.Services = []*structs.Service{{
   310  		Name:     "cn-service",
   311  		TaskName: tg.Tasks[0].Name,
   312  		Connect: &structs.ConsulConnect{
   313  			Native: true,
   314  		}},
   315  	}
   316  	tg.Tasks[0].Kind = structs.NewTaskKind("connect-native", "cn-service")
   317  
   318  	logger := testlog.HCLogger(t)
   319  
   320  	allocDir, cleanup := allocdir.TestAllocDir(t, logger, "ConnectNative", alloc.ID)
   321  	defer cleanup()
   322  
   323  	// register group services
   324  	consulConfig := consulapi.DefaultConfig()
   325  	consulConfig.Address = testConsul.HTTPAddr
   326  	consulAPIClient, err := consulapi.NewClient(consulConfig)
   327  	require.NoError(t, err)
   328  	namespacesClient := agentconsul.NewNamespacesClient(consulAPIClient.Namespaces(), consulAPIClient.Agent())
   329  
   330  	consulClient := agentconsul.NewServiceClient(consulAPIClient.Agent(), namespacesClient, logger, true)
   331  	go consulClient.Run()
   332  	defer consulClient.Shutdown()
   333  	require.NoError(t, consulClient.RegisterWorkload(agentconsul.BuildAllocServices(mock.Node(), alloc, agentconsul.NoopRestarter())))
   334  
   335  	// Run Connect Native hook
   336  	h := newConnectNativeHook(newConnectNativeHookConfig(alloc, &config.ConsulConfig{
   337  		Addr: consulConfig.Address,
   338  	}, logger))
   339  	request := &interfaces.TaskPrestartRequest{
   340  		Task:    tg.Tasks[0],
   341  		TaskDir: allocDir.NewTaskDir(tg.Tasks[0].Name),
   342  		TaskEnv: taskenv.NewEmptyTaskEnv(),
   343  	}
   344  	require.NoError(t, request.TaskDir.Build(false, nil))
   345  
   346  	response := new(interfaces.TaskPrestartResponse)
   347  
   348  	// Run the Connect Native hook
   349  	require.NoError(t, h.Prestart(context.Background(), request, response))
   350  
   351  	// Assert the hook is Done
   352  	require.True(t, response.Done)
   353  
   354  	// Assert only CONSUL_HTTP_ADDR env variable is set
   355  	require.Equal(t, map[string]string{"CONSUL_HTTP_ADDR": testConsul.HTTPAddr}, response.Env)
   356  
   357  	// Assert no secrets were written
   358  	checkFilesInDir(t, request.TaskDir.SecretsDir,
   359  		nil,
   360  		[]string{sidsTokenFile, secretCAFilename, secretCertfileFilename, secretKeyfileFilename},
   361  	)
   362  }
   363  
   364  func TestTaskRunner_ConnectNativeHook_with_SI_token(t *testing.T) {
   365  	ci.Parallel(t)
   366  	testutil.RequireConsul(t)
   367  
   368  	testConsul := getTestConsul(t)
   369  	defer testConsul.Stop()
   370  
   371  	alloc := mock.Alloc()
   372  	alloc.AllocatedResources.Shared.Networks = []*structs.NetworkResource{{Mode: "host", IP: "1.1.1.1"}}
   373  	tg := alloc.Job.TaskGroups[0]
   374  	tg.Services = []*structs.Service{{
   375  		Name:     "cn-service",
   376  		TaskName: tg.Tasks[0].Name,
   377  		Connect: &structs.ConsulConnect{
   378  			Native: true,
   379  		}},
   380  	}
   381  	tg.Tasks[0].Kind = structs.NewTaskKind("connect-native", "cn-service")
   382  
   383  	logger := testlog.HCLogger(t)
   384  
   385  	allocDir, cleanup := allocdir.TestAllocDir(t, logger, "ConnectNative", alloc.ID)
   386  	defer cleanup()
   387  
   388  	// register group services
   389  	consulConfig := consulapi.DefaultConfig()
   390  	consulConfig.Address = testConsul.HTTPAddr
   391  	consulAPIClient, err := consulapi.NewClient(consulConfig)
   392  	require.NoError(t, err)
   393  	namespacesClient := agentconsul.NewNamespacesClient(consulAPIClient.Namespaces(), consulAPIClient.Agent())
   394  
   395  	consulClient := agentconsul.NewServiceClient(consulAPIClient.Agent(), namespacesClient, logger, true)
   396  	go consulClient.Run()
   397  	defer consulClient.Shutdown()
   398  	require.NoError(t, consulClient.RegisterWorkload(agentconsul.BuildAllocServices(mock.Node(), alloc, agentconsul.NoopRestarter())))
   399  
   400  	// Run Connect Native hook
   401  	h := newConnectNativeHook(newConnectNativeHookConfig(alloc, &config.ConsulConfig{
   402  		Addr: consulConfig.Address,
   403  	}, logger))
   404  	request := &interfaces.TaskPrestartRequest{
   405  		Task:    tg.Tasks[0],
   406  		TaskDir: allocDir.NewTaskDir(tg.Tasks[0].Name),
   407  		TaskEnv: taskenv.NewEmptyTaskEnv(),
   408  	}
   409  	require.NoError(t, request.TaskDir.Build(false, nil))
   410  
   411  	// Insert service identity token in the secrets directory
   412  	token := uuid.Generate()
   413  	siTokenFile := filepath.Join(request.TaskDir.SecretsDir, sidsTokenFile)
   414  	err = ioutil.WriteFile(siTokenFile, []byte(token), 0440)
   415  	require.NoError(t, err)
   416  
   417  	response := new(interfaces.TaskPrestartResponse)
   418  	response.Env = make(map[string]string)
   419  
   420  	// Run the Connect Native hook
   421  	require.NoError(t, h.Prestart(context.Background(), request, response))
   422  
   423  	// Assert the hook is Done
   424  	require.True(t, response.Done)
   425  
   426  	// Assert environment variable for token is set
   427  	require.NotEmpty(t, response.Env)
   428  	require.Equal(t, token, response.Env["CONSUL_HTTP_TOKEN"])
   429  
   430  	// Assert no additional secrets were written
   431  	checkFilesInDir(t, request.TaskDir.SecretsDir,
   432  		[]string{sidsTokenFile},
   433  		[]string{secretCAFilename, secretCertfileFilename, secretKeyfileFilename},
   434  	)
   435  }
   436  
   437  func TestTaskRunner_ConnectNativeHook_shareTLS(t *testing.T) {
   438  	ci.Parallel(t)
   439  	testutil.RequireConsul(t)
   440  
   441  	try := func(t *testing.T, shareSSL *bool) {
   442  		fakeCert, _ := setupCertDirs(t)
   443  
   444  		testConsul := getTestConsul(t)
   445  		defer testConsul.Stop()
   446  
   447  		alloc := mock.Alloc()
   448  		alloc.AllocatedResources.Shared.Networks = []*structs.NetworkResource{{Mode: "host", IP: "1.1.1.1"}}
   449  		tg := alloc.Job.TaskGroups[0]
   450  		tg.Services = []*structs.Service{{
   451  			Name:     "cn-service",
   452  			TaskName: tg.Tasks[0].Name,
   453  			Connect: &structs.ConsulConnect{
   454  				Native: true,
   455  			}},
   456  		}
   457  		tg.Tasks[0].Kind = structs.NewTaskKind("connect-native", "cn-service")
   458  
   459  		logger := testlog.HCLogger(t)
   460  
   461  		allocDir, cleanup := allocdir.TestAllocDir(t, logger, "ConnectNative", alloc.ID)
   462  		defer cleanup()
   463  
   464  		// register group services
   465  		consulConfig := consulapi.DefaultConfig()
   466  		consulConfig.Address = testConsul.HTTPAddr
   467  		consulAPIClient, err := consulapi.NewClient(consulConfig)
   468  		require.NoError(t, err)
   469  		namespacesClient := agentconsul.NewNamespacesClient(consulAPIClient.Namespaces(), consulAPIClient.Agent())
   470  
   471  		consulClient := agentconsul.NewServiceClient(consulAPIClient.Agent(), namespacesClient, logger, true)
   472  		go consulClient.Run()
   473  		defer consulClient.Shutdown()
   474  		require.NoError(t, consulClient.RegisterWorkload(agentconsul.BuildAllocServices(mock.Node(), alloc, agentconsul.NoopRestarter())))
   475  
   476  		// Run Connect Native hook
   477  		h := newConnectNativeHook(newConnectNativeHookConfig(alloc, &config.ConsulConfig{
   478  			Addr: consulConfig.Address,
   479  
   480  			// TLS config consumed by native application
   481  			ShareSSL:  shareSSL,
   482  			EnableSSL: pointer.Of(true),
   483  			VerifySSL: pointer.Of(true),
   484  			CAFile:    fakeCert,
   485  			CertFile:  fakeCert,
   486  			KeyFile:   fakeCert,
   487  			Auth:      "user:password",
   488  			Token:     uuid.Generate(),
   489  		}, logger))
   490  		request := &interfaces.TaskPrestartRequest{
   491  			Task:    tg.Tasks[0],
   492  			TaskDir: allocDir.NewTaskDir(tg.Tasks[0].Name),
   493  			TaskEnv: taskenv.NewEmptyTaskEnv(), // nothing set in env stanza
   494  		}
   495  		require.NoError(t, request.TaskDir.Build(false, nil))
   496  
   497  		response := new(interfaces.TaskPrestartResponse)
   498  		response.Env = make(map[string]string)
   499  
   500  		// Run the Connect Native hook
   501  		require.NoError(t, h.Prestart(context.Background(), request, response))
   502  
   503  		// Assert the hook is Done
   504  		require.True(t, response.Done)
   505  
   506  		// Remove variables we are not interested in
   507  		delete(response.Env, "CONSUL_HTTP_ADDR")
   508  
   509  		// Assert environment variable for token is set
   510  		require.NotEmpty(t, response.Env)
   511  		require.Equal(t, map[string]string{
   512  			"CONSUL_CACERT":          "/secrets/consul_ca_file.pem",
   513  			"CONSUL_CLIENT_CERT":     "/secrets/consul_cert_file.pem",
   514  			"CONSUL_CLIENT_KEY":      "/secrets/consul_key_file.pem",
   515  			"CONSUL_HTTP_SSL":        "true",
   516  			"CONSUL_HTTP_SSL_VERIFY": "true",
   517  		}, response.Env)
   518  		require.NotContains(t, response.Env, "CONSUL_HTTP_AUTH")  // explicitly not shared
   519  		require.NotContains(t, response.Env, "CONSUL_HTTP_TOKEN") // explicitly not shared
   520  
   521  		// Assert 3 pem files were written
   522  		checkFilesInDir(t, request.TaskDir.SecretsDir,
   523  			[]string{secretCAFilename, secretCertfileFilename, secretKeyfileFilename},
   524  			[]string{sidsTokenFile},
   525  		)
   526  	}
   527  
   528  	// The default behavior is that share_ssl is true (similar to allow_unauthenticated)
   529  	// so make sure an unset value turns the feature on.
   530  
   531  	t.Run("share_ssl is true", func(t *testing.T) {
   532  		try(t, pointer.Of(true))
   533  	})
   534  
   535  	t.Run("share_ssl is nil", func(t *testing.T) {
   536  		try(t, nil)
   537  	})
   538  }
   539  
   540  func checkFilesInDir(t *testing.T, dir string, includes, excludes []string) {
   541  	ls, err := ioutil.ReadDir(dir)
   542  	require.NoError(t, err)
   543  
   544  	var present []string
   545  	for _, fInfo := range ls {
   546  		present = append(present, fInfo.Name())
   547  	}
   548  
   549  	for _, filename := range includes {
   550  		require.Contains(t, present, filename)
   551  	}
   552  	for _, filename := range excludes {
   553  		require.NotContains(t, present, filename)
   554  	}
   555  }
   556  
   557  func TestTaskRunner_ConnectNativeHook_shareTLS_override(t *testing.T) {
   558  	ci.Parallel(t)
   559  	testutil.RequireConsul(t)
   560  
   561  	fakeCert, _ := setupCertDirs(t)
   562  
   563  	testConsul := getTestConsul(t)
   564  	defer testConsul.Stop()
   565  
   566  	alloc := mock.Alloc()
   567  	alloc.AllocatedResources.Shared.Networks = []*structs.NetworkResource{{Mode: "host", IP: "1.1.1.1"}}
   568  	tg := alloc.Job.TaskGroups[0]
   569  	tg.Services = []*structs.Service{{
   570  		Name:     "cn-service",
   571  		TaskName: tg.Tasks[0].Name,
   572  		Connect: &structs.ConsulConnect{
   573  			Native: true,
   574  		}},
   575  	}
   576  	tg.Tasks[0].Kind = structs.NewTaskKind("connect-native", "cn-service")
   577  
   578  	logger := testlog.HCLogger(t)
   579  
   580  	allocDir, cleanup := allocdir.TestAllocDir(t, logger, "ConnectNative", alloc.ID)
   581  	defer cleanup()
   582  
   583  	// register group services
   584  	consulConfig := consulapi.DefaultConfig()
   585  	consulConfig.Address = testConsul.HTTPAddr
   586  	consulAPIClient, err := consulapi.NewClient(consulConfig)
   587  	require.NoError(t, err)
   588  	namespacesClient := agentconsul.NewNamespacesClient(consulAPIClient.Namespaces(), consulAPIClient.Agent())
   589  
   590  	consulClient := agentconsul.NewServiceClient(consulAPIClient.Agent(), namespacesClient, logger, true)
   591  	go consulClient.Run()
   592  	defer consulClient.Shutdown()
   593  	require.NoError(t, consulClient.RegisterWorkload(agentconsul.BuildAllocServices(mock.Node(), alloc, agentconsul.NoopRestarter())))
   594  
   595  	// Run Connect Native hook
   596  	h := newConnectNativeHook(newConnectNativeHookConfig(alloc, &config.ConsulConfig{
   597  		Addr: consulConfig.Address,
   598  
   599  		// TLS config consumed by native application
   600  		ShareSSL:  pointer.Of(true),
   601  		EnableSSL: pointer.Of(true),
   602  		VerifySSL: pointer.Of(true),
   603  		CAFile:    fakeCert,
   604  		CertFile:  fakeCert,
   605  		KeyFile:   fakeCert,
   606  		Auth:      "user:password",
   607  	}, logger))
   608  
   609  	taskEnv := taskenv.NewEmptyTaskEnv()
   610  	taskEnv.EnvMap = map[string]string{
   611  		"CONSUL_CACERT":          "/foo/ca.pem",
   612  		"CONSUL_CLIENT_CERT":     "/foo/cert.pem",
   613  		"CONSUL_CLIENT_KEY":      "/foo/key.pem",
   614  		"CONSUL_HTTP_AUTH":       "foo:bar",
   615  		"CONSUL_HTTP_SSL_VERIFY": "false",
   616  		"CONSUL_HTTP_ADDR":       "localhost:8500",
   617  		// CONSUL_HTTP_SSL (check the default value is assumed from client config)
   618  	}
   619  
   620  	request := &interfaces.TaskPrestartRequest{
   621  		Task:    tg.Tasks[0],
   622  		TaskDir: allocDir.NewTaskDir(tg.Tasks[0].Name),
   623  		TaskEnv: taskEnv, // env stanza is configured w/ non-default tls configs
   624  	}
   625  	require.NoError(t, request.TaskDir.Build(false, nil))
   626  
   627  	response := new(interfaces.TaskPrestartResponse)
   628  	response.Env = make(map[string]string)
   629  
   630  	// Run the Connect Native hook
   631  	require.NoError(t, h.Prestart(context.Background(), request, response))
   632  
   633  	// Assert the hook is Done
   634  	require.True(t, response.Done)
   635  
   636  	// Assert environment variable for CONSUL_HTTP_SSL is set, because it was
   637  	// the only one not overridden by task env stanza config
   638  	require.NotEmpty(t, response.Env)
   639  	require.Equal(t, map[string]string{
   640  		"CONSUL_HTTP_SSL": "true",
   641  	}, response.Env)
   642  
   643  	// Assert 3 pem files were written (even though they will be ignored)
   644  	// as this is gated by share_tls, not the presense of ca environment variables.
   645  	checkFilesInDir(t, request.TaskDir.SecretsDir,
   646  		[]string{secretCAFilename, secretCertfileFilename, secretKeyfileFilename},
   647  		[]string{sidsTokenFile},
   648  	)
   649  }