github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/integration_test/api_test.go (about)

     1  package integration_test
     2  
     3  import (
     4  	"context"
     5  	goRuntime "runtime"
     6  	"sync"
     7  	"unicode"
     8  	"unicode/utf8"
     9  
    10  	"github.com/google/go-cmp/cmp"
    11  	"github.com/google/go-cmp/cmp/cmpopts"
    12  	"helm.sh/helm/v3/pkg/cli/values"
    13  
    14  	"github.com/telepresenceio/telepresence/rpc/v2/connector"
    15  	"github.com/telepresenceio/telepresence/v2/integration_test/itest"
    16  	"github.com/telepresenceio/telepresence/v2/pkg/client"
    17  	"github.com/telepresenceio/telepresence/v2/pkg/client/cli"
    18  	"github.com/telepresenceio/telepresence/v2/pkg/client/cli/api"
    19  	"github.com/telepresenceio/telepresence/v2/pkg/client/cli/helm"
    20  	"github.com/telepresenceio/telepresence/v2/pkg/dos"
    21  	"github.com/telepresenceio/telepresence/v2/pkg/version"
    22  )
    23  
    24  type apiSuite struct {
    25  	itest.Suite
    26  	itest.NamespacePair
    27  	svc string
    28  }
    29  
    30  func (s *apiSuite) SuiteName() string {
    31  	return "API"
    32  }
    33  
    34  func init() {
    35  	itest.AddNamespacePairSuite("", func(h itest.NamespacePair) itest.TestingSuite {
    36  		return &apiSuite{Suite: itest.Suite{Harness: h}, NamespacePair: h, svc: "echo"}
    37  	})
    38  }
    39  
    40  func (s *apiSuite) request() api.ConnectRequest {
    41  	rq := api.ConnectRequest{}
    42  	rq.KubeFlags = map[string]string{
    43  		"namespace":  s.AppNamespace(),
    44  		"kubeconfig": itest.KubeConfig(s.Context()),
    45  	}
    46  	rq.ManagerNamespace = s.ManagerNamespace()
    47  	return rq
    48  }
    49  
    50  func (s *apiSuite) AmendSuiteContext(ctx context.Context) context.Context {
    51  	// The default executable will be the test executable. We need the telepresence executable here.
    52  	return dos.WithExe(ctx, s)
    53  }
    54  
    55  func (s *apiSuite) SetupSuite() {
    56  	s.Suite.SetupSuite()
    57  	wg := sync.WaitGroup{}
    58  	wg.Add(2)
    59  	go func() {
    60  		defer wg.Done()
    61  		ctx := s.Context()
    62  		hr := helm.Request{
    63  			Options: values.Options{
    64  				Values: s.GetValuesForHelm(ctx, nil, false),
    65  			},
    66  			Type: helm.Install,
    67  		}
    68  		s.Require().NoError(cli.NewClient(ctx).Helm(&hr, s.request()))
    69  	}()
    70  	go func() {
    71  		defer wg.Done()
    72  		s.ApplyEchoService(s.Context(), s.svc, 80)
    73  	}()
    74  	wg.Wait()
    75  }
    76  
    77  func (s *apiSuite) TearDownSuite() {
    78  	wg := sync.WaitGroup{}
    79  	wg.Add(2)
    80  	go func() {
    81  		defer wg.Done()
    82  		s.DeleteSvcAndWorkload(s.Context(), "deploy", s.svc)
    83  	}()
    84  	go func() {
    85  		defer wg.Done()
    86  		hr := helm.Request{Type: helm.Uninstall}
    87  		s.Require().NoError(cli.NewClient(s.Context()).Helm(&hr, s.request()))
    88  	}()
    89  	wg.Wait()
    90  }
    91  
    92  func (s *apiSuite) Test_Connect() {
    93  	rq := s.Require()
    94  	ctx := s.Context()
    95  	client := cli.NewClient(ctx)
    96  	conn, err := client.Connect(s.request())
    97  	rq.NoError(err)
    98  	defer func() {
    99  		s.NoError(conn.Disconnect())
   100  	}()
   101  	rq.Equal(conn.Info().Namespace, s.AppNamespace())
   102  }
   103  
   104  func (s *apiSuite) Test_List() {
   105  	rq := s.Require()
   106  	ctx := s.Context()
   107  	client := cli.NewClient(ctx)
   108  
   109  	conn, err := client.Connect(s.request())
   110  	rq.NoError(err)
   111  	defer func() {
   112  		s.NoError(conn.Disconnect())
   113  	}()
   114  
   115  	wfs, err := conn.List("")
   116  	rq.NoError(err)
   117  	rq.Len(wfs, 1)
   118  	rq.Equal(wfs[0].Name, s.svc)
   119  	rq.Equal(conn.Info().Namespace, s.AppNamespace())
   120  }
   121  
   122  func (s *apiSuite) Test_StartIntercept() {
   123  	rq := s.Require()
   124  	ctx := s.Context()
   125  	client := cli.NewClient(ctx)
   126  
   127  	conn, err := client.Connect(s.request())
   128  	rq.NoError(err)
   129  	defer func() {
   130  		s.NoError(conn.Disconnect())
   131  	}()
   132  
   133  	ii, err := conn.StartIntercept(api.InterceptRequest{
   134  		WorkloadName: "echo",
   135  		Port:         "8080",
   136  	}, "")
   137  	rq.NoError(err)
   138  	defer func() {
   139  		s.NoError(conn.EndIntercept(ii.Name))
   140  	}()
   141  	s.NotNil(ii.Environment)
   142  	s.Equal("echo-server", ii.Environment["TELEPRESENCE_CONTAINER"])
   143  }
   144  
   145  func (s *apiSuite) Test_RunIntercept() {
   146  	if s.IsCI() && !(goRuntime.GOOS == "linux" && goRuntime.GOARCH == "amd64") {
   147  		s.T().Skip("CI can't run linux docker containers inside non-linux runners")
   148  	}
   149  
   150  	ctx := itest.WithConfig(s.Context(), func(cfg client.Config) {
   151  		cfg.Intercept().UseFtp = false
   152  	})
   153  
   154  	rq := s.Require()
   155  	client := cli.NewClient(ctx)
   156  
   157  	cr := s.request()
   158  	cr.Docker = true
   159  	conn, err := client.Connect(cr)
   160  	rq.NoError(err)
   161  	defer func() {
   162  		s.NoError(conn.Disconnect())
   163  	}()
   164  
   165  	_, err = conn.RunIntercept(
   166  		api.InterceptRequest{
   167  			WorkloadName: "echo",
   168  			Port:         "8080",
   169  		},
   170  		api.DockerRunInterceptHandler{
   171  			Image:     "busybox",
   172  			Arguments: []string{"ls", "/var/run/secrets/kubernetes.io/serviceaccount"},
   173  		})
   174  	rq.NoError(err)
   175  }
   176  
   177  func (s *apiSuite) Test_Connections() {
   178  	if s.IsCI() && !(goRuntime.GOOS == "linux" && goRuntime.GOARCH == "amd64") {
   179  		s.T().Skip("CI can't run linux docker containers inside non-linux runners")
   180  	}
   181  
   182  	ctx := itest.WithConfig(s.Context(), func(cfg client.Config) {
   183  		cfg.Intercept().UseFtp = false
   184  	})
   185  
   186  	// Establish two connections
   187  	rq := s.Require()
   188  	client := cli.NewClient(ctx)
   189  	r1 := s.request()
   190  	r1.Name = "alpha"
   191  	r1.Docker = true
   192  	c1, err := client.Connect(r1)
   193  	rq.NoError(err)
   194  	defer func() {
   195  		s.NoError(c1.Disconnect())
   196  	}()
   197  
   198  	r2 := s.request()
   199  	r2.Name = "beta"
   200  	r2.Docker = true
   201  	r2.KubeFlags["namespace"] = s.ManagerNamespace()
   202  	c2, err := client.Connect(r2)
   203  	rq.NoError(err)
   204  	defer func() {
   205  		s.NoError(c2.Disconnect())
   206  	}()
   207  
   208  	// Verify that the two connections can be listed
   209  	conns, err := client.Connections()
   210  	rq.NoError(err)
   211  	rq.Equal(2, len(conns))
   212  
   213  	if conns[0].Name == r1.Name {
   214  		rq.Equal(conns[1].Name, r2.Name)
   215  	} else {
   216  		rq.Equal(conns[1].Name, r1.Name)
   217  		rq.Equal(conns[0].Name, r2.Name)
   218  	}
   219  
   220  	// Retrieve connection by name
   221  	cc1, err := client.Connection("alpha")
   222  	rq.NoError(err)
   223  
   224  	// The error is set to ALREADY_CONNECTED when the connection is already established. This is expected.
   225  	ignoreErr := cmpopts.IgnoreFields(connector.ConnectInfo{}, "Error")
   226  	ignoreUnEx := cmp.FilterPath(unexported, cmp.Ignore())
   227  
   228  	rq.True(cmp.Equal(c1.Info(), cc1.Info(), ignoreErr, ignoreUnEx), cmp.Diff(c1.Info(), cc1.Info(), ignoreErr, ignoreUnEx))
   229  
   230  	// Retrieve connection by name
   231  	cc2, err := client.Connection("beta")
   232  	rq.NoError(err)
   233  	rq.True(cmp.Equal(c2.Info(), cc2.Info(), ignoreErr, ignoreUnEx), cmp.Diff(c2.Info(), cc2.Info(), ignoreErr, ignoreUnEx))
   234  }
   235  
   236  func (s *apiSuite) Test_Version() {
   237  	client := cli.NewClient(s.Context())
   238  	s.Equal(version.Structured, client.Version())
   239  }
   240  
   241  func unexported(p cmp.Path) bool {
   242  	if f, ok := p.Index(-1).(cmp.StructField); ok {
   243  		r, _ := utf8.DecodeRuneInString(f.Name())
   244  		return !unicode.IsUpper(r)
   245  	}
   246  	return false
   247  }