├── .dockerignore ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── actions │ ├── install-dependencies │ │ └── action.yaml │ └── upload-logs │ │ └── action.yaml ├── pull_request_template.md └── workflows │ ├── dev.yaml │ ├── go-dependency-submission.yaml │ ├── image-scan.yaml │ ├── licenses.yaml │ ├── release.yaml │ ├── stale.yaml │ └── unit_tests.yaml ├── .gitignore ├── .golangci.yml ├── .mailmap ├── .protolint.yaml ├── CHANGELOG.OLD.md ├── CHANGELOG.yml ├── CODE-OF-CONDUCT.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── DEPENDENCIES.md ├── DEPENDENCY_LICENSES.md ├── GOVERNANCE-maintainer.md ├── LICENSE ├── MAINTAINERS.md ├── MEETING_SCHEDULE.md ├── Makefile ├── README.md ├── SECURITY.md ├── build-aux ├── admission_controller_tls │ └── main.go ├── docker │ └── images │ │ ├── Dockerfile.client │ │ ├── Dockerfile.traffic │ │ └── Dockerfile.winbuild ├── docs.mk ├── genversion │ └── main.go ├── image-importer.yaml ├── main.mk ├── maintenance.mk ├── prelude.mk ├── tools.mk ├── unparsable-packages.yaml └── winmake.bat ├── charts ├── chart.go └── telepresence-oss │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── agentInjectorWebhook.yaml │ ├── certificate.yaml │ ├── clientRbac │ │ ├── cluster-scope.yaml │ │ ├── connect.yaml │ │ └── namespace-scope.yaml │ ├── deployment.yaml │ ├── issuer.yaml │ ├── pre-delete-hook.yaml │ ├── service.yaml │ ├── tests │ │ └── test-connection.yaml │ ├── trafficManager-configmap.yaml │ └── trafficManagerRbac │ │ ├── cluster-scope.yaml │ │ ├── namespace-scope.yaml │ │ ├── service-account.yaml │ │ └── webhook-secret.yaml │ ├── values.schema.yaml │ └── values.yaml ├── cmd ├── telepresence │ └── main.go └── traffic │ ├── cmd │ ├── agent │ │ ├── agent.go │ │ ├── agent_test.go │ │ ├── client.go │ │ ├── config.go │ │ ├── containerstate.go │ │ ├── fwdstate.go │ │ ├── intercepttarget.go │ │ ├── server.go │ │ ├── state.go │ │ └── state_test.go │ ├── agentinit │ │ ├── agent_init.go │ │ └── agent_init_windows.go │ └── manager │ │ ├── cluster │ │ ├── id.go │ │ ├── info.go │ │ ├── info_test.go │ │ ├── nodewatcher.go │ │ ├── nodewatcher_test.go │ │ ├── podwatcher.go │ │ ├── podwatcher_test.go │ │ └── subscriber.go │ │ ├── config │ │ └── config.go │ │ ├── data.go │ │ ├── health.go │ │ ├── manager.go │ │ ├── managerutil │ │ ├── agentimage.go │ │ ├── ctx.go │ │ ├── data.go │ │ ├── data_test.go │ │ ├── envconfig.go │ │ └── envconfig_test.go │ │ ├── mutator │ │ ├── agent_injector.go │ │ ├── agent_injector_test.go │ │ ├── certgetter.go │ │ ├── eviction.go │ │ ├── service.go │ │ ├── service_watcher.go │ │ ├── watcher.go │ │ └── workload_watcher.go │ │ ├── namespaces │ │ └── watcher.go │ │ ├── service.go │ │ ├── service_test.go │ │ ├── state │ │ ├── assert_test.go │ │ ├── consumption.go │ │ ├── consumption_test.go │ │ ├── data.go │ │ ├── dns.go │ │ ├── intercept.go │ │ ├── llsubscriber.go │ │ ├── presence_test.go │ │ ├── session.go │ │ ├── state.go │ │ ├── state_test.go │ │ └── workload_info_watcher.go │ │ ├── test │ │ ├── load_testdata_internal.go │ │ └── testdata │ │ │ ├── agents.yaml │ │ │ ├── clients.yaml │ │ │ └── mechanisms.yaml │ │ └── watchable │ │ └── map.go │ └── main.go ├── docs ├── CONTRIBUTING.md ├── README.md ├── community.md ├── compare │ └── mirrord.md ├── concepts │ ├── devloop.md │ ├── faster.md │ └── intercepts.md ├── doc-links.yml ├── faqs.md ├── helm │ └── values.schema.json ├── howtos │ ├── cluster-in-vm.md │ ├── docker.md │ ├── engage.md │ └── large-clusters.md ├── images │ ├── TP_Architecture.svg │ ├── bugfix.png │ ├── change.png │ ├── container-inner-dev-loop.png │ ├── docker-header-containers.png │ ├── feature.png │ ├── logo.png │ ├── secondary-container-intercept.png │ ├── secondary-no-intercept.png │ ├── secondary-normal-intercept.png │ ├── security.png │ ├── split-tunnel.png │ ├── tracing.png │ ├── trad-inner-dev-loop.png │ ├── tunnelblick.png │ ├── vpn-dns.png │ ├── vpn-k8s-config.jpg │ ├── vpn-routing.jpg │ ├── vpn-vnat.jpg │ └── vpn-with-tele.jpg ├── install │ ├── client.md │ ├── cloud.md │ ├── manager.md │ └── upgrade.md ├── licenses.md ├── quick-start.md ├── redirects.yml ├── reference │ ├── architecture.md │ ├── client.md │ ├── cluster-config.md │ ├── config.md │ ├── dns.md │ ├── docker-run.md │ ├── engagements │ │ ├── cli.md │ │ ├── container.md │ │ └── sidecar.md │ ├── environment.md │ ├── inside-container.md │ ├── monitoring.md │ ├── rbac.md │ ├── routing.md │ ├── tun-device.md │ ├── volume.md │ └── vpn.md ├── release-notes.md ├── release-notes.mdx ├── troubleshooting.md └── variables.yml ├── go.mod ├── go.sum ├── integration_test ├── agent_injector_disabled_test.go ├── argo_rollouts_test.go ├── bind_to_podip_test.go ├── cidr_conflict_test.go ├── cli_test.go ├── cloud_config_test.go ├── connected_test.go ├── container_test.go ├── docker_daemon_test.go ├── docker_run_test.go ├── env_interpolate_test.go ├── gather_logs_test.go ├── headless_test.go ├── helm_test.go ├── ignored_mounts_test.go ├── ingest_test.go ├── inject_policy_test.go ├── injector_test.go ├── install_test.go ├── integration_test.go ├── intercept_env_test.go ├── intercept_flags_test.go ├── intercept_localhost_test.go ├── intercept_mount_test.go ├── itest │ ├── apply_app.go │ ├── assertions.go │ ├── cluster.go │ ├── config.go │ ├── connected.go │ ├── context.go │ ├── env.go │ ├── harness.go │ ├── helm.go │ ├── image.go │ ├── logdir.go │ ├── multiple_services.go │ ├── namespace.go │ ├── rm_as_root_unix.go │ ├── rm_as_root_windows.go │ ├── runner.go │ ├── single_service.go │ ├── status.go │ ├── suite.go │ ├── tempdir.go │ ├── template.go │ ├── timed.go │ └── traffic_manager.go ├── kubeauth_test.go ├── kubeconfig_extension_test.go ├── large_files_test.go ├── limitrange_test.go ├── list_watch_test.go ├── loglevel_test.go ├── manager_grpc_test.go ├── manual_agent_test.go ├── mounts_test.go ├── multi_connect_test.go ├── multiple_intercepts_test.go ├── multiple_port_intercept_test.go ├── multiple_services_test.go ├── multiport_test.go ├── namespaces_test.go ├── not_connected_test.go ├── pod_cidr_test.go ├── podscaling_test.go ├── proxy_via_test.go ├── reconnect_session_test.go ├── replace_test.go ├── single_service_test.go ├── subdomain_test.go ├── svcdomain_test.go ├── testdata │ ├── apiserveraccess │ │ └── main.go │ ├── cli-container │ │ └── Dockerfile │ ├── echo-server │ │ ├── Dockerfile │ │ ├── Dockerfile.debug │ │ ├── frontend.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── main.go │ ├── k8s │ │ ├── client_experiment.yaml │ │ ├── client_rancher.goyaml │ │ ├── client_sa.yaml │ │ ├── disruption-budget.goyaml │ │ ├── echo-argo-rollout.yaml │ │ ├── echo-auto-inject.yaml │ │ ├── echo-both.yaml │ │ ├── echo-double-one-unnamed.yaml │ │ ├── echo-easy.yaml │ │ ├── echo-extra-udp.yaml │ │ ├── echo-headless.yaml │ │ ├── echo-interpolate.yaml │ │ ├── echo-manual-inject-deploy.yaml │ │ ├── echo-manual-inject-svc.yaml │ │ ├── echo-min.yaml │ │ ├── echo-no-containerport.yaml │ │ ├── echo-no-svc-ann.yaml │ │ ├── echo-no-svc.yaml │ │ ├── echo-no-vols.yaml │ │ ├── echo-one.yaml │ │ ├── echo-same-target-port.yaml │ │ ├── echo-secondary.yaml │ │ ├── echo-udp-tcp-unnamed.yaml │ │ ├── echo-w-hostalias.goyaml │ │ ├── echo-w-sidecars.yaml │ │ ├── echo-w-subdomain.yaml │ │ ├── echo_with_env.yaml │ │ ├── generic.goyaml │ │ ├── hello-w-volumes.goyaml │ │ ├── many-volumes.yaml │ │ ├── memory-constraints.yaml │ │ ├── namespaces.yaml │ │ ├── pol-disabled.yaml │ │ ├── pol-enabled.yaml │ │ ├── pol-none.yaml │ │ ├── pv.goyaml │ │ ├── pvc.goyaml │ │ ├── rs-echo.yaml │ │ ├── secret-reader-rbac.goyaml │ │ ├── ss-echo.yaml │ │ ├── svc-deploy.goyaml │ │ └── with-probes.yaml │ ├── k8screds │ │ └── main.go │ ├── routing-values.yaml │ ├── scripts │ │ ├── veth-down.sh │ │ └── veth-up.sh │ ├── stdiotest │ │ └── main.go │ ├── telepresence-1.9.9.tgz │ └── udp-echo │ │ ├── Dockerfile │ │ ├── go.mod │ │ └── main.go ├── to_pod_test.go ├── udp_test.go ├── uhn_dns_test.go ├── uninstall_test.go ├── webhook_test.go ├── wiretap_test.go ├── workload_configuration_test.go ├── workloads_test.go ├── workspace_watch_test.go └── wpad_test.go ├── k8s ├── agent-injector-rbac.yaml ├── agent-injector-secret.yaml ├── agent-injector.yaml ├── apitest.yaml ├── dnsutils-headless.yaml ├── echo-auto-headless.yaml ├── echo-double-one.yaml ├── echo-double.yaml ├── echo-sc.yaml ├── ext-example.yaml ├── hello-w-volume.yaml ├── manager.yaml ├── minikube-registry.yaml ├── private-reg-proxy.yaml └── rs-echo-svc2.yaml ├── packaging ├── artifacthub-repo.yml ├── bundle.wxs.in ├── helmpackage.go ├── homebrew-oss-formula.rb ├── homebrew-package.sh ├── install-telepresence.ps1 ├── sidebar.png ├── telepresence.wxs.in └── windows-package.sh ├── pkg ├── agentconfig │ ├── container.go │ ├── container_test.go │ ├── initcontainer.go │ ├── injectpolicy.go │ ├── sidecar.go │ ├── util.go │ └── volumes.go ├── agentmap │ ├── capsbase26.go │ ├── capsbase26_test.go │ ├── discorvery.go │ └── generator.go ├── annotation │ └── annotation.go ├── authenticator │ ├── authenticator.go │ ├── authenticator_test.go │ ├── config.go │ ├── exec.go │ ├── exec_test.go │ ├── grpc │ │ └── authenticator.go │ ├── mocks │ │ ├── clientconfig_mock.go │ │ └── credentialsresolver_mock.go │ └── patcher │ │ └── patcher.go ├── client │ ├── agentpf │ │ └── clients.go │ ├── bwcompat │ │ └── clusterinfo.go │ ├── cache │ │ ├── cache.go │ │ ├── ingresses.go │ │ └── watcher.go │ ├── cli │ │ ├── ann │ │ │ └── annotations.go │ │ ├── cmd │ │ │ ├── completion.go │ │ │ ├── config.go │ │ │ ├── connect.go │ │ │ ├── curl.go │ │ │ ├── docker_run.go │ │ │ ├── gather_logs.go │ │ │ ├── gather_logs_test.go │ │ │ ├── genyaml.go │ │ │ ├── helm.go │ │ │ ├── ingest.go │ │ │ ├── intercept.go │ │ │ ├── kubeauth.go │ │ │ ├── leave.go │ │ │ ├── list.go │ │ │ ├── list_contexts.go │ │ │ ├── list_namespaces.go │ │ │ ├── loglevel.go │ │ │ ├── quit.go │ │ │ ├── replace.go │ │ │ ├── status.go │ │ │ ├── telepresence.go │ │ │ ├── testdata │ │ │ │ ├── license │ │ │ │ ├── testLogDir │ │ │ │ │ ├── cli.log │ │ │ │ │ ├── connector-20210916T130347.log │ │ │ │ │ ├── connector-20210916T130356.log │ │ │ │ │ ├── connector-20210916T130624.log │ │ │ │ │ ├── connector-20210916T130643.log │ │ │ │ │ ├── connector.log │ │ │ │ │ ├── daemon-20210916T130318.log │ │ │ │ │ ├── daemon-20210916T130402.log │ │ │ │ │ ├── daemon-20210916T130624.log │ │ │ │ │ ├── daemon-20210916T130643.log │ │ │ │ │ ├── daemon.log │ │ │ │ │ ├── echo-auto-inject-6496f77cbd-n86nc │ │ │ │ │ └── traffic-manager-5c69859f94-g4ntj │ │ │ │ └── zipDir │ │ │ │ │ ├── diff_name.log │ │ │ │ │ ├── file1.log │ │ │ │ │ └── file2.log │ │ │ ├── uninstall.go │ │ │ ├── usage.go │ │ │ ├── version.go │ │ │ └── wiretap.go │ │ ├── connect │ │ │ ├── connector.go │ │ │ ├── daemon.go │ │ │ ├── init_command.go │ │ │ └── version_check.go │ │ ├── daemon │ │ │ ├── identifier.go │ │ │ ├── identifier_test.go │ │ │ ├── info.go │ │ │ ├── request.go │ │ │ ├── request_test.go │ │ │ └── userd.go │ │ ├── docker │ │ │ ├── auto_complete.go │ │ │ ├── flags.go │ │ │ ├── published_ports.go │ │ │ ├── run_flags.go │ │ │ └── runner.go │ │ ├── env │ │ │ ├── flags.go │ │ │ ├── syntax.go │ │ │ └── syntax_test.go │ │ ├── flags │ │ │ ├── context.go │ │ │ ├── deprecation.go │ │ │ ├── map.go │ │ │ ├── unparsed.go │ │ │ └── unparsed_test.go │ │ ├── global │ │ │ └── flags.go │ │ ├── helm │ │ │ ├── chart.go │ │ │ ├── install.go │ │ │ ├── lint.go │ │ │ └── release.go │ │ ├── ingest │ │ │ ├── command.go │ │ │ ├── info.go │ │ │ └── state.go │ │ ├── intercept │ │ │ ├── command.go │ │ │ ├── describe_intercepts.go │ │ │ ├── info.go │ │ │ ├── result.go │ │ │ └── state.go │ │ ├── main.go │ │ ├── mount │ │ │ ├── flags.go │ │ │ ├── info.go │ │ │ ├── prepare_unix.go │ │ │ └── prepare_windows.go │ │ ├── output │ │ │ ├── output.go │ │ │ └── output_test.go │ │ └── progress │ │ │ ├── colors.go │ │ │ ├── event.go │ │ │ ├── json.go │ │ │ ├── noop.go │ │ │ ├── plain.go │ │ │ ├── quiet.go │ │ │ ├── spinner.go │ │ │ ├── tty.go │ │ │ ├── tty_test.go │ │ │ └── writer.go │ ├── cmd_error.go │ ├── config.go │ ├── config_darwin.go │ ├── config_linux.go │ ├── config_test.go │ ├── config_unix.go │ ├── config_util.go │ ├── config_windows.go │ ├── const.go │ ├── docker │ │ ├── container.go │ │ ├── context.go │ │ ├── daemon.go │ │ ├── daemon_test.go │ │ ├── image.go │ │ ├── kubeauth │ │ │ └── cmd.go │ │ ├── network.go │ │ ├── volume.go │ │ └── volume_test.go │ ├── ensured_state.go │ ├── envconfig.go │ ├── envconfig_unix.go │ ├── envconfig_windows.go │ ├── free_ports.go │ ├── install_id.go │ ├── k8s_config.go │ ├── k8sclient │ │ ├── cani.go │ │ ├── config.go │ │ └── connect.go │ ├── logging │ │ ├── cached_timed_level.go │ │ ├── dup_test.go │ │ ├── dup_unix.go │ │ ├── dup_windows.go │ │ ├── initcontext.go │ │ ├── initcontext_test.go │ │ ├── initcontext_unix_test.go │ │ ├── initcontext_windows_test.go │ │ ├── rotatingfile.go │ │ ├── rotatingfile_unix.go │ │ ├── rotatingfile_windows.go │ │ ├── stat.go │ │ ├── stat_darwin.go │ │ ├── stat_linux.go │ │ ├── stat_linux_test.go │ │ ├── stat_test.go │ │ └── stat_windows.go │ ├── portforward │ │ ├── borrowed_kubectl_cmdutil.go │ │ ├── grpcresolver.go │ │ ├── podaddr.go │ │ ├── resolve.go │ │ └── streamconn.go │ ├── remotefs │ │ ├── bridge.go │ │ ├── fuseftp.go │ │ ├── fuseftp_docker.go │ │ ├── fuseftp_embedded.go │ │ ├── fuseftp_external.go │ │ ├── fuseftp_grpc.go │ │ ├── fuseftp_linked.go │ │ ├── mounter.go │ │ └── sftp.go │ ├── rootd │ │ ├── dbus │ │ │ └── resolved.go │ │ ├── dns │ │ │ ├── client_queue.go │ │ │ ├── connpool.go │ │ │ ├── connpool_test.go │ │ │ ├── resolved_linux.go │ │ │ ├── server.go │ │ │ ├── server_darwin.go │ │ │ ├── server_linux.go │ │ │ ├── server_test.go │ │ │ └── server_windows.go │ │ ├── in_process.go │ │ ├── service.go │ │ ├── session.go │ │ ├── stream_creator.go │ │ └── vip │ │ │ ├── env_nat.go │ │ │ ├── env_nat_test.go │ │ │ └── vip.go │ ├── scout │ │ └── reporter.go │ ├── socket │ │ ├── sockets.go │ │ ├── sockets_test.go │ │ ├── sockets_unix.go │ │ └── sockets_windows.go │ ├── stream_error.go │ ├── userd │ │ ├── context.go │ │ ├── daemon │ │ │ ├── grpc.go │ │ │ ├── mgrproxy.go │ │ │ └── service.go │ │ ├── k8s │ │ │ ├── k8s_cluster.go │ │ │ └── outbound.go │ │ ├── service.go │ │ ├── session.go │ │ └── trafficmgr │ │ │ ├── agents.go │ │ │ ├── config.go │ │ │ ├── dial_request.go │ │ │ ├── gather_logs.go │ │ │ ├── ingest.go │ │ │ ├── intercept.go │ │ │ ├── mount.go │ │ │ ├── podaccess.go │ │ │ ├── session.go │ │ │ └── session_info_cache.go │ ├── version.go │ └── version_test.go ├── dnsproxy │ ├── lookup.go │ ├── lookup_test.go │ ├── resolvefile_unix.go │ └── rpc.go ├── dos │ ├── aferofs │ │ └── wrapper.go │ ├── env.go │ ├── exe.go │ ├── filesystem.go │ ├── filesystem_test.go │ ├── lockedfile.go │ ├── package.go │ ├── stdio.go │ └── wdwrapper.go ├── dpipe │ ├── dpipe.go │ ├── dpipe_test.go │ ├── dpipe_unix.go │ ├── dpipe_windows.go │ └── testdata │ │ └── echo │ │ └── echo.go ├── errcat │ └── errors.go ├── filelocation │ ├── .editorconfig │ ├── app.go │ ├── borrowed_os_file.go │ ├── ctx.go │ ├── misc.go │ └── os_file_test.go ├── forwarder │ ├── interceptor.go │ ├── tcp.go │ ├── udp.go │ └── wiretap.go ├── grpc │ └── server │ │ ├── logging.go │ │ └── server.go ├── informer │ ├── context.go │ └── factory.go ├── ioutil │ ├── keyvalueformatter.go │ ├── print.go │ ├── safename.go │ ├── tempname.go │ └── writeallto.go ├── ipproto │ └── ipproto.go ├── iputil │ ├── ipnet.go │ ├── ips.go │ ├── join.go │ ├── normalize.go │ └── parse.go ├── k8sapi │ ├── appprotostrat.go │ ├── cani.go │ ├── clientconfigprovider.go │ ├── interface.go │ ├── kind.go │ ├── namespaceid.go │ ├── object.go │ ├── util.go │ └── workload.go ├── labels │ └── selector.go ├── log │ ├── base_logger.go │ ├── discard.go │ ├── formatter.go │ ├── level.go │ ├── testlogger.go │ └── timed_level.go ├── maps │ └── utils.go ├── matcher │ ├── header_stringer.go │ ├── header_stringer_test.go │ ├── headers.go │ ├── headers_test.go │ ├── request.go │ ├── request_test.go │ └── value.go ├── pprof │ └── pprof.go ├── proc │ ├── cmd.go │ ├── docker_linux.go │ ├── docker_other.go │ ├── exec.go │ ├── exec_unix.go │ ├── exec_windows.go │ ├── wsl_linux.go │ └── wsl_other.go ├── restapi │ ├── api.go │ └── api_test.go ├── routing │ ├── routing.go │ ├── routing_darwin.go │ ├── routing_linux.go │ ├── routing_test.go │ ├── routing_unix.go │ ├── routing_unix_test.go │ └── routing_windows.go ├── shellquote │ ├── shellstring.go │ ├── shellstring_unix.go │ ├── shellstring_unix_test.go │ ├── shellstring_windows.go │ └── shellstring_windows_test.go ├── slice │ ├── contains.go │ └── csv.go ├── subnet │ ├── bitfield256.go │ ├── bitfield256_test.go │ ├── set.go │ ├── set_test.go │ ├── subnet.go │ ├── subnet_test.go │ └── testdata │ │ └── ips.txt ├── tunnel │ ├── bidipipe.go │ ├── client_stream.go │ ├── connid.go │ ├── connid_test.go │ ├── context.go │ ├── dialer.go │ ├── message.go │ ├── metrics.go │ ├── pipe.go │ ├── pool.go │ ├── probe.go │ ├── provider.go │ ├── server_stream.go │ ├── stream.go │ ├── stream_test.go │ ├── timed_handler.go │ └── udplistener.go ├── types │ ├── engagement.go │ ├── mountpolicy.go │ ├── portandproto.go │ ├── portidentifier.go │ └── portmapping.go ├── version │ └── version.go ├── vif │ ├── device.go │ ├── device_darwin.go │ ├── device_linux.go │ ├── device_notlinux.go │ ├── device_unix.go │ ├── device_windows.go │ ├── logging.go │ ├── router.go │ ├── router_test.go │ ├── stack.go │ ├── testdata │ │ └── router │ │ │ ├── .gitignore │ │ │ ├── go.mod │ │ │ ├── go.sum │ │ │ └── main.go │ └── tunneling_device.go └── workload │ ├── informers.go │ ├── state.go │ ├── util.go │ └── watcher.go ├── rpc ├── LICENSE ├── agent │ ├── agent.pb.go │ ├── agent.proto │ └── agent_grpc.pb.go ├── authenticator │ ├── authenticator.pb.go │ ├── authenticator.proto │ └── authenticator_grpc.pb.go ├── common │ ├── errors.pb.go │ ├── errors.proto │ ├── version.pb.go │ └── version.proto ├── connector │ ├── connector.pb.go │ ├── connector.proto │ └── connector_grpc.pb.go ├── daemon │ ├── daemon.pb.go │ ├── daemon.proto │ └── daemon_grpc.pb.go ├── go.mod ├── go.sum └── manager │ ├── manager.pb.go │ ├── manager.proto │ └── manager_grpc.pb.go ├── test-infra ├── aws-okd │ ├── README.md │ └── dns │ │ ├── .gitignore │ │ └── dns.tf └── aws-vpn │ ├── .gitignore │ ├── README.md │ ├── dns.tf │ ├── eks.tf │ ├── locals.tf │ ├── network.tf │ ├── pki.sh │ ├── provider.tf │ └── vpn.tf └── tools └── src ├── go-mkopensource ├── go.mod ├── go.sum └── pin.go ├── gosimports ├── go.mod ├── go.sum └── pin.go ├── protoc-gen-go-grpc ├── go.mod ├── go.sum └── pin.go ├── protoc-gen-go ├── go.mod ├── go.sum └── pin.go ├── relnotesgen ├── go.mod ├── go.sum ├── main.go └── relnotes │ ├── changelog.go │ ├── relnotes.gomd │ └── relnotes.gomdx ├── test-report ├── go.mod ├── go.sum ├── logger.go ├── main.go └── progress.go ├── tocgen └── main.go └── y2j ├── go.mod ├── go.sum └── main.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .circleci/ 2 | .idea/ 3 | .git/ 4 | build-aux/ 5 | build-output/ 6 | !build-output/version.txt 7 | integration_test/ 8 | k8s/ 9 | packaging/ 10 | test-infra/ 11 | tools/ 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | 9 | [*.go] 10 | indent_style = tab 11 | ; mimic gofmt's go/printer.printer.funcBody().maxSize 12 | max_line_length = 100 13 | ; mimic gofmt's cmd/gofmt.tabWidth 14 | tab_width = 8 15 | 16 | [{go.mod,go.sum}] 17 | indent_style = tab 18 | 19 | [*.proto] 20 | indent_style = space 21 | indent_size = 2 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Always check out with LF so that golangci-lint doesn't break 2 | *.go text eol=lf -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [thallgren] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for Telepresence 4 | 5 | --- 6 | 7 | **Please describe your use case / problem.** 8 | A clear and concise description of your use case / problem. The "why" is really valuable to us. For example, "I have a set of services whose clients have long-lived connections." 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Versions (please complete the following information)** 17 | - Output of `telepresence version` (just in case the feature exists in future versions) 18 | - Kubernetes Environment and Version 19 | 20 | **Additional context** 21 | Add any other context about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/actions/upload-logs/action.yaml: -------------------------------------------------------------------------------- 1 | name: "Upload logs" 2 | description: "Upload logs from the test run" 3 | runs: 4 | using: composite 5 | steps: 6 | - run: | 7 | LOGS="test-logs/${{ runner.os }}/${{ runner.arch }}" 8 | mkdir -p "$LOGS" 9 | if [[ $RUNNER_OS != Windows ]]; then 10 | rsync -ma --include='*/' --include='*.tap' --include='*.log' --include='Test*.webm' --exclude='*' . "$LOGS" 11 | fi 12 | for file in \ 13 | {"${XDG_CACHE_HOME:-$HOME/.cache}/telepresence/logs","$HOME/Library/Logs/telepresence","$LOCALAPPDATA/telepresence/logs","."}/*.log 14 | do 15 | if [ -s "$file" ]; then 16 | cp -v "$file" "$LOGS" || true 17 | fi 18 | done 19 | shell: bash 20 | name: Gather logs 21 | - name: Upload logs 22 | uses: actions/upload-artifact@v4 23 | with: 24 | # If an environment variable LOG_SUFFIX is set, it will be appended to the log filename. 25 | name: ${{github.job}}-logs-${{ env.LOG_SUFFIX }} 26 | path: | 27 | test-logs/${{ runner.os }}/${{ runner.arch }}/* 28 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | A few sentences describing the overall goals of the pull request's commits. 4 | 5 | ## Checklist 6 | 7 | 11 | 12 | - [ ] I made sure to update `./CHANGELOG.yml` (our release notes are generated from this file). 13 | - [ ] I made sure to add any documentation changes required for my change. 14 | - [ ] My change is adequately tested. 15 | - [ ] I updated `CONTRIBUTING.md` with any special dev tricks I had to use to work on this code efficiently. 16 | - [ ] Once my PR is ready to have integration tests ran, I posted the PR in #telepresence-oss channel on the 17 | [CNCF Slack](https://slack.cncf.io/) so that the "ok to test" label can be applied. -------------------------------------------------------------------------------- /.github/workflows/go-dependency-submission.yaml: -------------------------------------------------------------------------------- 1 | name: Go Dependency Submission 2 | on: 3 | push: 4 | branches: 5 | - release/v2 6 | 7 | # The API requires write permission on the repository to submit dependencies 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | go-action-detection: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: 'Checkout Repository' 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - uses: actions/setup-go@v5 20 | with: 21 | go-version: stable 22 | # we need fuseftp.bits 23 | - name: "Build dependencies" 24 | run: make build-deps 25 | 26 | - name: Run snapshot action 27 | uses: actions/go-dependency-submission@v1 28 | with: 29 | go-mod-path: go.mod 30 | go-build-target: cmd/telepresence/main.go 31 | -------------------------------------------------------------------------------- /.github/workflows/image-scan.yaml: -------------------------------------------------------------------------------- 1 | name: image-scan 2 | on: 3 | push: 4 | branches: 5 | - release/v2 6 | pull_request_target: 7 | jobs: 8 | trivy-container-scan: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | ref: ${{ github.event.pull_request.head.sha }} 15 | - uses: actions/setup-go@v5 16 | with: 17 | go-version: stable 18 | - name: Build dev image 19 | run: | 20 | make save-tel2-image 21 | - name: Scan 22 | uses: aquasecurity/trivy-action@master 23 | with: 24 | input: build-output/tel2-image.tar 25 | format: sarif 26 | exit-code: 0 # only warn for now until we have backed it into our processes 27 | output: trivy-results.sarif 28 | ignore-unfixed: true 29 | vuln-type: "os,library" 30 | severity: "CRITICAL,HIGH" 31 | hide-progress: false 32 | - name: Upload Scan to GitHub Security Tab 33 | uses: github/codeql-action/upload-sarif@v3 34 | with: 35 | sarif_file: "trivy-results.sarif" 36 | pass: 37 | name: image-scan 38 | needs: 39 | - trivy-container-scan 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: No-Op 43 | if: ${{ false }} 44 | run: "echo Pass" 45 | -------------------------------------------------------------------------------- /.github/workflows/stale.yaml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PR' 2 | on: 3 | schedule: 4 | - cron: '12 10 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in 7 days.' 13 | stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment, or this will be closed in 14 days.' 14 | close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' 15 | close-pr-message: 'This PR was closed because it has been stalled for 14 days with no activity.' 16 | days-before-issue-stale: 60 17 | days-before-pr-stale: 30 18 | days-before-issue-close: 7 19 | days-before-pr-close: 14 20 | operations-per-run: 80 21 | exempt-issue-labels: feature,friction 22 | -------------------------------------------------------------------------------- /.github/workflows/unit_tests.yaml: -------------------------------------------------------------------------------- 1 | name: "Build and Unit test" 2 | on: 3 | pull_request_target: 4 | env: 5 | HOMEBREW_NO_INSTALL_FROM_API: 6 | jobs: 7 | unit: 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | runners: 12 | - ubuntu-latest 13 | - macos-latest 14 | - windows-latest 15 | runs-on: ${{ matrix.runners }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | ref: "${{ github.event.pull_request.head.sha }}" 21 | - name: install dependencies 22 | uses: ./.github/actions/install-dependencies 23 | - name: install build dependencies 24 | run: make build-deps 25 | - name: Lint 26 | if: ${{ runner.os != 'Windows' }} 27 | uses: golangci/golangci-lint-action@v7 28 | with: 29 | args: --timeout 8m ./... 30 | - name: Lint (limited on windows) 31 | if: ${{ runner.os == 'Windows' }} 32 | uses: golangci/golangci-lint-action@v7 33 | with: 34 | args: --timeout 8m ./cmd/telepresence/... ./integration_test/... ./pkg/... 35 | - name: Build 36 | run: make build 37 | - name: Run tests 38 | uses: nick-fields/retry/@v3 39 | with: 40 | max_attempts: 3 41 | timeout_minutes: 12 42 | command: make check-unit 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories 15 | vendor/ 16 | 17 | # Editor nonsense 18 | .vscode 19 | .idea 20 | *.iml 21 | *.swp 22 | .DS_Store 23 | 24 | build-output 25 | /tools/* 26 | !/tools/src/ 27 | 28 | # Don't accidentally add files from `go build` 29 | telepresence 30 | traffic 31 | tst-manager 32 | !/cmd/*/ 33 | 34 | # Include Chart directory 35 | !/charts/telepresence-oss 36 | # But don't include this one 37 | /charts/telepresence-oss/k8s-defs.json 38 | 39 | # Downloaded fuseftp bits 40 | fuseftp.bits 41 | 42 | # Needed to build on windows 43 | .wintools 44 | .gocache 45 | 46 | # Don't accidentally add binaries built byintegration tests 47 | /integration_test/testdata/echo-server/echo-server 48 | /build-aux/genversion/genversion 49 | 50 | # Vagrant 51 | .vagrant/ 52 | tests.log 53 | 54 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Alex Gervais alex 2 | Alex Gervais 3 | Donny Yung 4 | Flynn 5 | John Esmet 6 | Luke Shumaker 7 | Maxime Legault-Venne 8 | Rafael Schloming 9 | Thomas Hallgren 10 | 11 | Abhay Saxena 12 | Alvaro Saurin <1841612+inercia@users.noreply.github.com> 13 | Philip Lombardi plombardi 14 | Philip Lombardi <893096+plombardi89@users.noreply.github.com> 15 | Philip Lombardi 16 | -------------------------------------------------------------------------------- /.protolint.yaml: -------------------------------------------------------------------------------- 1 | lint: 2 | # Which rules to enable/disable. 3 | rules: 4 | remove: 5 | - ENUM_FIELD_NAMES_PREFIX 6 | # Settings for those rules. 7 | rules_option: 8 | max_line_length: 9 | max_chars: 120 10 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We follow the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). 4 | 5 | Please contact the [CNCF Code of Conduct Committee](mailto:conduct@cncf.io) 6 | in order to report violations of the Code of Conduct. -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default codeowners for the telepresenceio/telepresence repository 2 | * @telepresenceio/telepresence 3 | -------------------------------------------------------------------------------- /DEPENDENCY_LICENSES.md: -------------------------------------------------------------------------------- 1 | Telepresence CLI incorporates Free and Open Source software under the following licenses: 2 | 3 | * [2-clause BSD license](https://opensource.org/licenses/BSD-2-Clause) 4 | * [3-clause BSD license](https://opensource.org/licenses/BSD-3-Clause) 5 | * [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) 6 | * [ISC license](https://opensource.org/licenses/ISC) 7 | * [MIT license](https://opensource.org/licenses/MIT) 8 | * [Mozilla Public License 2.0](https://opensource.org/licenses/MPL-2.0) 9 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Telepresence Maintainers 2 | 3 | [GOVERNANCE.md](GOVERNANCE.md) describes governance guidelines and 4 | maintainer responsibilities. 5 | 6 | ## Maintainers 7 | 8 | Maintainers are listed in alphabetical order. 9 | 10 | | Maintainer | GitHub ID | Affiliation | 11 | |-----------------| ----------------------------------------- |-----------------| 12 | | Jakub Rożek | [P0lip](https://github.com/P0lip) | Ambassador Labs | 13 | | Nick Powell | [njayp](https://github.com/njayp) | | 14 | | Thomas Hallgren | [thallgren](https://github.com/thallgren) | | 15 | 16 | ## Maintainers Emeriti 17 | 18 | * Abhay Saxena ([ark3](https://github.com/ark3)) 19 | * Donny Yung ([donnyyung](https://github.com/donnyyung)) 20 | * Guillaume Veschambre ([shepz](https://github.com/shepz)) 21 | * Jose Cortes ([josecv](https://github.com/josecv)) 22 | * Kévin Lambert ([knlambert](https://github.com/knlambert)) 23 | * Luke Shumaker ([LukeShu](https://github.com/LukeShu)) 24 | * Rafael Schloming ([rhs](https://github.com/rhs)) 25 | * Raphael Reyna ([raphaelreyna](https://github.com/raphaelreyna)) 26 | * Richard Li ([richarddli](https://github.com/richarddli)) 27 | * Sarabraj Singh ([sarabrajsingh](https://github.com/sarabrajsingh)) 28 | -------------------------------------------------------------------------------- /MEETING_SCHEDULE.md: -------------------------------------------------------------------------------- 1 | # Community Meeting Schedule 2 | 3 | ## Weekly Troubleshooting 4 | 5 | We hold troubleshooting sessions once a week on Tuesdays, at 2:30 pm Eastern. These sessions are a way to connect in person with project maintainers and get help with any problems you might be encountering while using Telepresence. 6 | 7 | **Zoom Meeting Link**: https://us02web.zoom.us/j/81514139559?pwd=M3g3dzV4cytQV0IrMVpuZWFyU2ZDQT09 8 | 9 | 10 | ## Monthly Contributors Meeting 11 | 12 | The Telepresence Contributors Meeting is held on the first Wednesday of every month at 11am Eastern. The focus of this meeting is discussion of technical issues related to development of Telepresence. 13 | 14 | New contributors are always welcome! Check out our [contributor's guide](CONTRIBUTING.md) to learn how you can help make Telepresence better. 15 | 16 | **Zoom Meeting Link**: https://us02web.zoom.us/j/6297823847 17 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Security updates will be provided for the latest 2.x release. 6 | 7 | 8 | ### How do we handle vulnerabilities 9 | 10 | #### User reports 11 | 12 | If you discover any security vulnerabilities, please follow these guidelines: 13 | 14 | - Email your findings to [secalert@datawire.io](secalert@datawire.io). 15 | - Provide sufficient details, including steps to reproduce the vulnerability. 16 | - Do not publicly disclose the issue until we have had a chance to address it. 17 | 18 | #### Dependabot 19 | 20 | We run dependabot against our repo. We also have it create PRs with the updates. 21 | 22 | One of the maintainers responsibilities is to review these PRs, make any necessary updates, 23 | and merge them in so that they go out in our next set of releases. 24 | 25 | #### Keeping Go updated 26 | 27 | We're set up to receive embargoed security announcements for Golang. When it happens, 28 | we create a new security incident, evaluate if we're impacted, and release a hotfix as soon as possible. 29 | 30 | -------------------------------------------------------------------------------- /build-aux/docker/images/Dockerfile.winbuild: -------------------------------------------------------------------------------- 1 | FROM golang:1.19.4 2 | 3 | 4 | RUN apt-get update && \ 5 | apt-get install -y \ 6 | apt-transport-https \ 7 | ca-certificates \ 8 | curl \ 9 | gnupg \ 10 | lsb-release && \ 11 | curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \ 12 | echo \ 13 | "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ 14 | $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null 15 | 16 | RUN apt-get update && \ 17 | apt-get install -y docker-ce docker-ce-cli containerd.io -------------------------------------------------------------------------------- /build-aux/image-importer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | creationTimestamp: null 5 | labels: 6 | app: image-importer 7 | name: image-importer 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: image-importer 13 | strategy: {} 14 | template: 15 | metadata: 16 | creationTimestamp: null 17 | labels: 18 | app: image-importer 19 | spec: 20 | containers: 21 | - image: ubuntu 22 | name: ubuntu 23 | resources: {} 24 | command: 25 | - sleep 26 | - "10000000" 27 | volumeMounts: 28 | - mountPath: /run/k3s 29 | name: run 30 | - mountPath: /var/lib/rancher 31 | name: rancher 32 | - mountPath: /hostbin 33 | name: bin 34 | volumes: 35 | - name: run 36 | hostPath: 37 | path: /run/k3s 38 | - name: rancher 39 | hostPath: 40 | path: /var/lib/rancher 41 | - name: bin 42 | hostPath: 43 | path: /bin 44 | status: {} 45 | -------------------------------------------------------------------------------- /build-aux/maintenance.mk: -------------------------------------------------------------------------------- 1 | 2 | 3 | update-dependencies: $(dir $(shell find . -name go.mod)) 4 | for dir in $?; do\ 5 | (cd $$dir && \ 6 | (go get -u ./... && \ 7 | (grep -q gvisor.dev go.mod && go get -u gvisor.dev/gvisor@go) \ 8 | ) || \ 9 | go get -u .);\ 10 | done 11 | $(MAKE) clobber check-unit generate 12 | 13 | -------------------------------------------------------------------------------- /build-aux/unparsable-packages.yaml: -------------------------------------------------------------------------------- 1 | github.com/fclairamb/go-log: 2 | - MIT 3 | github.com/fclairamb/go-log/noop: 4 | - MIT 5 | github.com/fclairamb/ftpserverlib: 6 | - MIT 7 | github.com/Masterminds/squirrel: 8 | - MIT 9 | github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors: 10 | - Apache-2.0 11 | github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging: 12 | - Apache-2.0 13 | -------------------------------------------------------------------------------- /charts/telepresence-oss/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /charts/telepresence-oss/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: telepresence-oss 3 | description: A chart for deploying the server-side components of Telepresence 4 | type: application 5 | version: "1.1.1-bogus.overwritten.by.chart.go" 6 | icon: https://raw.githubusercontent.com/telepresenceio/telepresence.io/master/src/assets/images/telepresence-edgy.svg 7 | 8 | # Note: This is the version of the Traffic Manager that will be installed by 9 | # this chart. The telepresence CLI will always attempt to update the Traffic 10 | # Manager if it is not the same version as the CLI so ensure you are keeping 11 | # these in sync. 12 | appVersion: "1.1.1-bogus.overwritten.by.chart.go" 13 | 14 | annotations: 15 | artifacthub.io/license: Apache-2.0 -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | Congratulations! 3 | 4 | 5 | You have successfully installed the Traffic Manager component of Telepresence! 6 | Now your users will be able to `telepresence connect` to this Cluster and create 7 | intercepts for their services! 8 | 9 | -------------------------------------------------------------------------------- 10 | Next Steps 11 | -------------------------------------------------------------------------------- 12 | 13 | - Take a look at our RBAC documentation for setting up the minimal required RBAC 14 | roles for your users at https://www.telepresence.io/docs/reference/rbac 15 | 16 | - Ensure that you are keeping up to date with Telepresence releases 17 | https://github.com/telepresenceio/telepresence/releases so that your Traffic 18 | Manager is the same version as the telepresence client your users are running! 19 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/certificate.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (eq .Values.agentInjector.certificate.method "certmanager") .Values.agentInjector.enabled }} 2 | apiVersion: cert-manager.io/v1 3 | kind: Certificate 4 | metadata: 5 | name: {{ .Values.agentInjector.secret.name }} 6 | spec: 7 | secretName: {{ .Values.agentInjector.secret.name }} 8 | dnsNames: 9 | - {{ (printf "%s.%s" .Values.agentInjector.name .Release.Namespace ) }} 10 | - {{ (printf "%s.%s.svc" .Values.agentInjector.name .Release.Namespace ) }} 11 | {{- with .Values.agentInjector.certificate.certmanager }} 12 | {{- toYaml . | nindent 2 }} 13 | {{- end }} 14 | {{- end }} 15 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/clientRbac/cluster-scope.yaml: -------------------------------------------------------------------------------- 1 | {{- /* 2 | These are the cluster-wide rbac roles + bindings that will be used by users 3 | who want to use telepresence once its components have been set 4 | up in the cluster. 5 | */}} 6 | {{- with .Values.clientRbac }} 7 | {{- if (and .create (not (or .namespaces (include "traffic-manager.namespaced" $)))) }} 8 | {{- $roleName := include "telepresence.clientRbacName" $ }} 9 | 10 | --- 11 | kind: ClusterRole 12 | apiVersion: rbac.authorization.k8s.io/v1 13 | metadata: 14 | name: {{ $roleName }} 15 | labels: 16 | {{- include "telepresence.labels" $ | nindent 4 }} 17 | rules: 18 | - apiGroups: [""] 19 | resources: ["namespaces"] 20 | verbs: ["get", "list", "watch"] 21 | {{- include "telepresence.clientRbacInterceptRules" $ }} 22 | 23 | --- 24 | apiVersion: rbac.authorization.k8s.io/v1 25 | kind: ClusterRoleBinding 26 | metadata: 27 | name: {{ $roleName }} 28 | labels: 29 | {{- include "telepresence.labels" $ | nindent 4 }} 30 | subjects: 31 | {{ toYaml .subjects }} 32 | roleRef: 33 | kind: ClusterRole 34 | name: {{ $roleName }} 35 | apiGroup: rbac.authorization.k8s.io 36 | 37 | {{- end }} 38 | {{- end }} 39 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/clientRbac/connect.yaml: -------------------------------------------------------------------------------- 1 | {{- with .Values.clientRbac }} 2 | {{- if .create }} 3 | {{- /* 4 | Client must have the following RBAC in the traffic-manager.namespace to establish 5 | a port-forward to the traffic-manager pod. 6 | */}} 7 | kind: Role 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | metadata: 10 | name: traffic-manager-connect 11 | namespace: {{ include "traffic-manager.namespace" $ }} 12 | labels: 13 | {{- include "telepresence.labels" $ | nindent 4 }} 14 | rules: 15 | - apiGroups: [""] 16 | resources: ["pods"] 17 | verbs: ["get", "list"] 18 | - apiGroups: [""] 19 | resources: ["services"] 20 | resourceNames: 21 | - {{ include "traffic-manager.name" $ }} 22 | verbs: ["get"] 23 | - apiGroups: [""] 24 | resources: ["pods/portforward"] 25 | verbs: ["create"] 26 | --- 27 | 28 | apiVersion: rbac.authorization.k8s.io/v1 29 | kind: RoleBinding 30 | metadata: 31 | name: traffic-manager-connect 32 | namespace: {{ include "traffic-manager.namespace" $ }} 33 | labels: 34 | {{- include "telepresence.labels" $ | nindent 4 }} 35 | subjects: 36 | {{ toYaml .subjects }} 37 | roleRef: 38 | apiGroup: rbac.authorization.k8s.io 39 | name: traffic-manager-connect 40 | kind: Role 41 | 42 | {{- end }} 43 | {{- end }} 44 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/issuer.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (eq .Values.agentInjector.certificate.method "certmanager") .Values.agentInjector.enabled }} 2 | apiVersion: cert-manager.io/v1 3 | kind: {{ .Values.agentInjector.certificate.certmanager.issuerRef.kind }} 4 | metadata: 5 | name: {{ .Values.agentInjector.certificate.certmanager.issuerRef.name }} 6 | spec: 7 | selfSigned: {} 8 | {{- end }} 9 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- if not (and .Values.rbac .Values.rbac.only) }} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: "{{ include "traffic-manager.name" $ }}-test-connection" 6 | namespace: {{ include "traffic-manager.namespace" $ }} 7 | labels: 8 | {{- include "telepresence.labels" $ | nindent 4 }} 9 | annotations: 10 | "helm.sh/hook": test-success 11 | spec: 12 | {{- with .Values.hooks.busybox.imagePullSecrets }} 13 | imagePullSecrets: 14 | {{- toYaml . | nindent 2 }} 15 | {{- end }} 16 | containers: 17 | - name: wget 18 | image: "{{ .Values.hooks.busybox.registry }}/{{ .Values.hooks.busybox.image }}:{{ .Values.hooks.busybox.tag }}" 19 | command: ['wget'] 20 | args: ['{{ include "traffic-manager.name" $ }}:8081'] 21 | restartPolicy: Never 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManager-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "traffic-manager.name" $ }} 5 | namespace: {{ include "traffic-manager.namespace" $ }} 6 | labels: 7 | {{- include "telepresence.labels" $ | nindent 4 }} 8 | data: 9 | {{- if .Values.client }} 10 | client.yaml: | 11 | {{- toYaml .Values.client | nindent 4 }} 12 | {{- end }} 13 | {{- with .Values.intercept }} 14 | {{- if .environment }} 15 | agent-env.yaml: | 16 | {{- toYaml .environment | nindent 4 }} 17 | {{- end }} 18 | {{- end }} 19 | namespace-selector.yaml: | 20 | {{- toYaml (mustFromJson (include "traffic-manager.namespaceSelector" $)) | nindent 4 }} 21 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManagerRbac/service-account.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.managerRbac.create }} 2 | {{- /* This file contains the serviceAccount used for the traffic-manager deployment. */}} 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | name: traffic-manager 7 | namespace: {{ include "traffic-manager.namespace" $ }} 8 | labels: 9 | {{- include "telepresence.labels" $ | nindent 4 }} 10 | 11 | {{- end }} 12 | -------------------------------------------------------------------------------- /charts/telepresence-oss/templates/trafficManagerRbac/webhook-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (not (eq .Values.agentInjector.certificate.accessMethod "mount")) .Values.agentInjector.enabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | namespace: {{ include "traffic-manager.namespace" $ }} 6 | name: agent-injector-webhook-secret 7 | labels: {{- include "telepresence.labels" $ | nindent 4 }} 8 | rules: 9 | - apiGroups: 10 | - "" 11 | resources: 12 | - secrets 13 | resourceNames: [ {{ .Values.agentInjector.secret.name }} ] 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | 19 | --- 20 | apiVersion: rbac.authorization.k8s.io/v1 21 | kind: RoleBinding 22 | metadata: 23 | name: agent-injector-webhook-secret 24 | namespace: {{ include "traffic-manager.namespace" $ }} 25 | labels: {{- include "telepresence.labels" $ | nindent 4 }} 26 | roleRef: 27 | apiGroup: rbac.authorization.k8s.io 28 | kind: Role 29 | name: agent-injector-webhook-secret 30 | subjects: 31 | - kind: ServiceAccount 32 | name: traffic-manager 33 | namespace: {{ include "traffic-manager.namespace" $ }} 34 | {{- end }} -------------------------------------------------------------------------------- /cmd/telepresence/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli" 7 | ) 8 | 9 | func main() { 10 | cli.Main(cli.InitContext(context.Background())) 11 | } 12 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/agentinit/agent_init_windows.go: -------------------------------------------------------------------------------- 1 | package agentinit 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | ) 7 | 8 | // This file needs to exist because agent_init.go imports iptables, which obviously doesn't exist on windows. 9 | // It really doesn't matter, since there's no support for windows-based containers of any sort, but it'll fail the build. 10 | 11 | // Main is the main function for the agent init container. 12 | func Main(ctx context.Context, args ...string) error { 13 | return fmt.Errorf("windows-based init agent is not a thing") 14 | } 15 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/cluster/id.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "context" 5 | 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | v1 "k8s.io/client-go/kubernetes/typed/core/v1" 8 | ) 9 | 10 | var GetInstallIDFunc = GetNamespaceID //nolint:gochecknoglobals // extension point 11 | 12 | func GetNamespaceID(ctx context.Context, client v1.CoreV1Interface, namespace string) (string, error) { 13 | ns, err := client.Namespaces().Get(ctx, namespace, metav1.GetOptions{}) 14 | if err == nil { 15 | return string(ns.GetUID()), nil 16 | } 17 | return "", err 18 | } 19 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/health.go: -------------------------------------------------------------------------------- 1 | package manager 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/health/grpc_health_v1" 7 | ) 8 | 9 | // Perhaps replace this health check stuff with something more normal, i.e. 10 | // based on HTTP, since we'll likely be running the Injector as an HTTP service 11 | // from this same executable anyhow. 12 | 13 | type HealthChecker struct { 14 | grpc_health_v1.UnimplementedHealthServer 15 | } 16 | 17 | func (s *HealthChecker) Check(ctx context.Context, _ *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { 18 | return &grpc_health_v1.HealthCheckResponse{ 19 | Status: grpc_health_v1.HealthCheckResponse_SERVING, 20 | }, nil 21 | } 22 | 23 | func (s *HealthChecker) Watch(_ *grpc_health_v1.HealthCheckRequest, stream grpc_health_v1.Health_WatchServer) error { 24 | return stream.Send(&grpc_health_v1.HealthCheckResponse{ 25 | Status: grpc_health_v1.HealthCheckResponse_SERVING, 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/managerutil/ctx.go: -------------------------------------------------------------------------------- 1 | package managerutil 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/datawire/dlib/dlog" 7 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 8 | "github.com/telepresenceio/telepresence/v2/pkg/tunnel" 9 | ) 10 | 11 | func WithSessionInfo(ctx context.Context, si *manager.SessionInfo) context.Context { 12 | if id := si.GetSessionId(); id != "" { 13 | return WithSessionID(ctx, tunnel.SessionID(id)) 14 | } 15 | return ctx 16 | } 17 | 18 | func WithSessionID(ctx context.Context, sessionID tunnel.SessionID) context.Context { 19 | ctx = context.WithValue(ctx, sessionContextKey{}, sessionID) 20 | ctx = dlog.WithField(ctx, "session_id", sessionID) 21 | return ctx 22 | } 23 | 24 | func GetSessionID(ctx context.Context) tunnel.SessionID { 25 | if id, ok := ctx.Value(sessionContextKey{}).(tunnel.SessionID); ok { 26 | return id 27 | } 28 | return "" 29 | } 30 | 31 | type sessionContextKey struct{} 32 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/state/data.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | rpc "github.com/telepresenceio/telepresence/rpc/v2/manager" 5 | ) 6 | 7 | func agentHasMechanism(agent *rpc.AgentInfo, mechName string) bool { 8 | for _, mechanism := range agent.Mechanisms { 9 | if mechanism.Name == mechName { 10 | return true 11 | } 12 | } 13 | 14 | return false 15 | } 16 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/test/testdata/clients.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | alice: 3 | name: alice@squirtle.bigcorp.com 4 | namespace: default 5 | install_id: install_id_for_alice 6 | product: telepresence 7 | version: "2.14.0" 8 | bob: 9 | name: bob@bulbasaur.bigcorp.com 10 | namespace: default 11 | install_id: install_id_for_bob 12 | product: telepresence 13 | version: "2.14.0" 14 | cameron: 15 | name: cameron@charmander.bigcorp.com 16 | namespace: default 17 | install_id: install_id_for_cameron 18 | product: telepresence 19 | version: "2.14.0" 20 | pat: 21 | name: pat@pikachu.bigcorp.com 22 | namespace: default 23 | install_id: install_id_for_pat 24 | product: telepresence 25 | version: "2.14.0" 26 | license_key: license_key_for_pat 27 | -------------------------------------------------------------------------------- /cmd/traffic/cmd/manager/test/testdata/mechanisms.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | tcp: 3 | name: tcp 4 | product: oss 5 | version: "1" 6 | http: 7 | name: http 8 | product: plus 9 | version: "1" 10 | httpv2: 11 | name: http 12 | product: plus 13 | version: "2" 14 | grpc: 15 | name: grpc 16 | product: plus 17 | version: "1" 18 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Telepresence Documentation 2 | 3 | This folder contains the Telepresence documentation in a format suitable for a versioned folder in the 4 | telepresenceio/telepresence.io repository. The folder will show up in that repository when a new minor revision 5 | tag is created here. 6 | 7 | Assuming that a 2.20.0 release is pending, and that a release/v2.20.0 branch has been created, then: 8 | ```console 9 | $ export TELEPRESENCE_VERSION=v2.20.0 10 | $ make prepare-release 11 | $ git push origin {,rpc/}v2.20.0 release/v2.20.0 12 | ``` 13 | 14 | will result in a `docs/v2.20` folder with this folder's contents in the telepresenceio/telepresence.io repository. 15 | 16 | Subsequent bugfix tags for the same minor tag, i.e.: 17 | ```console 18 | $ export TELEPRESENCE_VERSION=v2.20.1 19 | $ make prepare-release 20 | $ git push origin {,rpc/}v2.20.1 release/v2.20.1 21 | ``` 22 | will not result in a new folder when it is pushed, but it will update the content of the `docs/v2.20` folder to 23 | reflect this folder's content for that tag. 24 | -------------------------------------------------------------------------------- /docs/community.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Community 3 | hide_table_of_contents: true 4 | --- 5 | 6 | # Community 7 | 8 | ## Contributor's guide 9 | Please review our [contributor's guide](https://github.com/telepresenceio/telepresence/blob/release/v2/CONTRIBUTING.md) 10 | on GitHub to learn how you can help make Telepresence better. 11 | 12 | ## Meetings 13 | Check out our community [meeting schedule](https://github.com/telepresenceio/telepresence/blob/release/v2/MEETING_SCHEDULE.md) for opportunities to interact with Telepresence developers. 14 | -------------------------------------------------------------------------------- /docs/images/bugfix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/bugfix.png -------------------------------------------------------------------------------- /docs/images/change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/change.png -------------------------------------------------------------------------------- /docs/images/container-inner-dev-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/container-inner-dev-loop.png -------------------------------------------------------------------------------- /docs/images/docker-header-containers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/docker-header-containers.png -------------------------------------------------------------------------------- /docs/images/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/feature.png -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/secondary-container-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/secondary-container-intercept.png -------------------------------------------------------------------------------- /docs/images/secondary-no-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/secondary-no-intercept.png -------------------------------------------------------------------------------- /docs/images/secondary-normal-intercept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/secondary-normal-intercept.png -------------------------------------------------------------------------------- /docs/images/security.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/security.png -------------------------------------------------------------------------------- /docs/images/split-tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/split-tunnel.png -------------------------------------------------------------------------------- /docs/images/tracing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/tracing.png -------------------------------------------------------------------------------- /docs/images/trad-inner-dev-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/trad-inner-dev-loop.png -------------------------------------------------------------------------------- /docs/images/tunnelblick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/tunnelblick.png -------------------------------------------------------------------------------- /docs/images/vpn-dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/vpn-dns.png -------------------------------------------------------------------------------- /docs/images/vpn-k8s-config.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/vpn-k8s-config.jpg -------------------------------------------------------------------------------- /docs/images/vpn-routing.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/vpn-routing.jpg -------------------------------------------------------------------------------- /docs/images/vpn-vnat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/vpn-vnat.jpg -------------------------------------------------------------------------------- /docs/images/vpn-with-tele.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/docs/images/vpn-with-tele.jpg -------------------------------------------------------------------------------- /docs/licenses.md: -------------------------------------------------------------------------------- 1 | Telepresence CLI incorporates Free and Open Source software under the following licenses: 2 | 3 | * [2-clause BSD license](https://opensource.org/licenses/BSD-2-Clause) 4 | * [3-clause BSD license](https://opensource.org/licenses/BSD-3-Clause) 5 | * [Apache License 2.0](https://opensource.org/licenses/Apache-2.0) 6 | * [ISC license](https://opensource.org/licenses/ISC) 7 | * [MIT license](https://opensource.org/licenses/MIT) 8 | * [Mozilla Public License 2.0](https://opensource.org/licenses/MPL-2.0) 9 | -------------------------------------------------------------------------------- /docs/quick-start.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quick start 3 | description: "Start using Telepresence in your own environment. Follow these steps to intercept your service in your cluster." 4 | hide_table_of_contents: true 5 | --- 6 | 7 | # Telepresence Quickstart 8 | 9 | Telepresence is an open source tool that enables you to set up remote development environments for Kubernetes where you can still use all of your favorite local tools like IDEs, debuggers, and profilers. 10 | 11 | ## Prerequisites 12 | 13 | - [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/), the Kubernetes command-line tool, or the OpenShift Container Platform command-line interface, [oc](https://docs.openshift.com/container-platform/4.2/cli_reference/openshift_cli/getting-started-cli.html#cli-installing-cli_cli-developer-commands). 14 | - A Kubernetes Deployment and Service. 15 | 16 | ## Install Telepresence 17 | 18 | Follow [Install Client](install/client.md) and [Install Traffic Manager](install/manager.md) instructions to install the 19 | telepresence client on your workstation, and the traffic manager in your cluster. 20 | 21 | Checkout the [Howto](howtos/engage.md) to learn how Telepresence can engage with resources in your remote cluster, 22 | enabling you to run the code on your local workstation. 23 | 24 | ## What’s Next? 25 | - [Learn about the Telepresence architecture.](reference/architecture) 26 | -------------------------------------------------------------------------------- /docs/redirects.yml: -------------------------------------------------------------------------------- 1 | - {from: "", to: "quick-start"} 2 | -------------------------------------------------------------------------------- /docs/variables.yml: -------------------------------------------------------------------------------- 1 | version: "2.23.0" 2 | dlVersion: "v2.23.0" 3 | -------------------------------------------------------------------------------- /integration_test/env_interpolate_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "github.com/go-json-experiment/json" 5 | core "k8s.io/api/core/v1" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | ) 9 | 10 | func (s *connectedSuite) Test_PrefixInterpolated() { 11 | ctx := s.Context() 12 | svc := "echo-interpolate" 13 | rq := s.Require() 14 | s.ApplyApp(ctx, svc, "deploy/"+svc) 15 | defer func() { 16 | s.DeleteSvcAndWorkload(ctx, "deploy", svc) 17 | s.NoError(s.Kubectl(ctx, "delete", "configmap", "interpolate-config")) 18 | }() 19 | 20 | itest.TelepresenceOk(ctx, "intercept", "--mount", "false", svc) 21 | defer itest.TelepresenceOk(ctx, "leave", svc) 22 | out, err := s.KubectlOut(ctx, "get", "pod", "-o", "json", "-l", "app="+svc) 23 | rq.NoError(err) 24 | 25 | var pods core.PodList 26 | err = json.Unmarshal([]byte(out), &pods) 27 | rq.NoError(err) 28 | var ag *core.Container 29 | outer: 30 | for _, pod := range pods.Items { 31 | cns := pod.Spec.Containers 32 | for ci := range cns { 33 | cn := &cns[ci] 34 | if cn.Name == "traffic-agent" { 35 | ag = cn 36 | break outer 37 | } 38 | } 39 | } 40 | rq.NotNil(ag) 41 | for _, vm := range ag.VolumeMounts { 42 | if vm.Name == "my-volume" { 43 | rq.Equal("$(_TEL_APP_A_SOME_NAME)_$(_TEL_APP_A_OTHER_NAME)", vm.SubPathExpr) 44 | break 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /integration_test/integration_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | ) 9 | 10 | func Test_Integration(t *testing.T) { 11 | ossRoot, err := filepath.Abs(".") 12 | if err != nil { 13 | t.Fatalf("unable to get absolute path of .oss: %v", err) 14 | } 15 | itest.RunTests(itest.TestContext(t, ossRoot, filepath.Dir(ossRoot))) 16 | } 17 | -------------------------------------------------------------------------------- /integration_test/itest/assertions.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | type Requirements struct { 12 | *require.Assertions 13 | } 14 | 15 | func (r *Requirements) EventuallyContext(ctx context.Context, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) { 16 | r.Eventually(func() bool { 17 | if ctx.Err() != nil { 18 | return true 19 | } 20 | return condition() 21 | }, waitFor, tick, msgAndArgs...) 22 | r.NoError(ctx.Err()) 23 | } 24 | 25 | type Assertions struct { 26 | *assert.Assertions 27 | } 28 | 29 | func (r *Assertions) EventuallyContext(ctx context.Context, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { 30 | return r.Eventually(func() bool { 31 | if ctx.Err() != nil { 32 | return true 33 | } 34 | return condition() 35 | }, waitFor, tick, msgAndArgs...) || r.NoError(ctx.Err()) 36 | } 37 | -------------------------------------------------------------------------------- /integration_test/itest/config.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/client" 9 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 10 | ) 11 | 12 | // SetConfig creates a config from the configYml provided and assigns it to a new context which 13 | // is returned. Use this if you are testing components of the config.yml, otherwise you can use setDefaultConfig. 14 | func SetConfig(ctx context.Context, configDir, configYml string) (context.Context, error) { 15 | config, err := os.Create(filepath.Join(configDir, "config.yml")) 16 | if err != nil { 17 | return ctx, err 18 | } 19 | 20 | _, err = config.WriteString(configYml) 21 | if err != nil { 22 | return ctx, err 23 | } 24 | config.Close() 25 | 26 | // Load env if it isn't loaded already 27 | ctx = filelocation.WithAppUserConfigDir(ctx, configDir) 28 | if env := client.GetEnv(ctx); env == nil { 29 | env, err = client.LoadEnv() 30 | if err != nil { 31 | return ctx, err 32 | } 33 | ctx = client.WithEnv(ctx, env) 34 | } 35 | 36 | cfg, err := client.LoadConfig(ctx) 37 | if err != nil { 38 | return ctx, err 39 | } 40 | ctx = client.WithConfig(ctx, cfg) 41 | return ctx, nil 42 | } 43 | -------------------------------------------------------------------------------- /integration_test/itest/connected.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | type connected struct { 12 | TrafficManager 13 | } 14 | 15 | func WithConnected(np TrafficManager, f func(ctx context.Context, ch TrafficManager)) { 16 | np.HarnessT().Run("Test_Connected", func(t *testing.T) { 17 | ctx := WithT(np.HarnessContext(), t) 18 | require.NoError(t, np.GeneralError()) 19 | ch := &connected{TrafficManager: np} 20 | ch.PushHarness(ctx, ch.setup, ch.tearDown) 21 | defer ch.PopHarness() 22 | f(ctx, ch) 23 | }) 24 | } 25 | 26 | func (ch *connected) setup(ctx context.Context) bool { 27 | t := getT(ctx) 28 | // Connect using telepresence-test-developer user 29 | stdout, _, err := Telepresence(ctx, "connect", "--namespace", ch.AppNamespace(), "--manager-namespace", ch.ManagerNamespace()) 30 | assert.NoError(t, err) 31 | assert.Contains(t, stdout, "Connected to context") 32 | if t.Failed() { 33 | return false 34 | } 35 | _, _, err = Telepresence(ctx, "loglevel", "-d30m", "debug") 36 | assert.NoError(t, err) 37 | return !t.Failed() 38 | } 39 | 40 | func (ch *connected) tearDown(ctx context.Context) { 41 | TelepresenceQuitOk(ctx) 42 | } 43 | -------------------------------------------------------------------------------- /integration_test/itest/logdir.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "regexp" 9 | 10 | "github.com/datawire/dlib/dlog" 11 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 12 | ) 13 | 14 | func CleanLogDir(ctx context.Context, require *Requirements, nsRx, mgrNamespace, svcNameRx string) { 15 | logDir := filelocation.AppUserLogDir(ctx) 16 | files, err := os.ReadDir(logDir) 17 | require.NoError(err) 18 | match := regexp.MustCompile( 19 | fmt.Sprintf(`^(?:traffic-manager-[0-9a-z-]+\.%s|%s-[0-9a-z-]+\.%s)\.(?:log|yaml)$`, 20 | mgrNamespace, svcNameRx, nsRx)) 21 | 22 | for _, file := range files { 23 | if match.MatchString(file.Name()) { 24 | dlog.Infof(ctx, "Deleting log-file %s", file.Name()) 25 | require.NoError(os.Remove(filepath.Join(logDir, file.Name()))) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /integration_test/itest/rm_as_root_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package itest 5 | 6 | //nolint:depguard // don't care about output or contexts 7 | import ( 8 | "context" 9 | "os/exec" 10 | ) 11 | 12 | func rmAsRoot(ctx context.Context, file string) error { 13 | return exec.Command("sudo", "-n", "rm", "-f", file).Run() 14 | } 15 | -------------------------------------------------------------------------------- /integration_test/itest/rm_as_root_windows.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "os" 6 | 7 | "golang.org/x/sys/windows" 8 | 9 | "github.com/telepresenceio/telepresence/v2/pkg/shellquote" 10 | ) 11 | 12 | func rmAsRoot(ctx context.Context, file string) error { 13 | cwd, _ := os.Getwd() 14 | // UTF16PtrFromString can only fail if the argument contains a NUL byte. That will never happen here. 15 | verbPtr, _ := windows.UTF16PtrFromString("runas") 16 | exePtr, _ := windows.UTF16PtrFromString("del") 17 | cwdPtr, _ := windows.UTF16PtrFromString(cwd) 18 | argPtr, _ := windows.UTF16PtrFromString(shellquote.ShellArgsString(append([]string{"/f", "/q"}, file))) 19 | return windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, windows.SW_HIDE) 20 | } 21 | -------------------------------------------------------------------------------- /integration_test/itest/single_service.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | type SingleService interface { 10 | NamespacePair 11 | ServiceName() string 12 | } 13 | 14 | type singleService struct { 15 | NamespacePair 16 | serviceName string 17 | } 18 | 19 | func WithSingleService(h NamespacePair, serviceName string, f func(SingleService)) { 20 | h.HarnessT().Run(fmt.Sprintf("Test_Service_%s", serviceName), func(t *testing.T) { 21 | ctx := WithT(h.HarnessContext(), t) 22 | s := &singleService{NamespacePair: h, serviceName: serviceName} 23 | s.PushHarness(ctx, s.setup, s.tearDown) 24 | defer h.PopHarness() 25 | f(s) 26 | }) 27 | } 28 | 29 | func (h *singleService) setup(ctx context.Context) bool { 30 | h.ApplyEchoService(ctx, h.serviceName, 80) 31 | return true 32 | } 33 | 34 | func (h *singleService) tearDown(ctx context.Context) { 35 | h.DeleteSvcAndWorkload(ctx, "deploy", h.serviceName) 36 | } 37 | 38 | func (h *singleService) ServiceName() string { 39 | return h.serviceName 40 | } 41 | -------------------------------------------------------------------------------- /integration_test/itest/suite.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/stretchr/testify/suite" 7 | ) 8 | 9 | type TestingSuite interface { 10 | suite.TestingSuite 11 | Harness 12 | AmendSuiteContext(context.Context) context.Context 13 | Context() context.Context 14 | Assert() *Assertions 15 | Require() *Requirements 16 | SuiteName() string 17 | setContext(ctx context.Context) 18 | } 19 | 20 | type Suite struct { 21 | suite.Suite 22 | Harness 23 | ctx context.Context 24 | } 25 | 26 | func (s *Suite) AmendSuiteContext(ctx context.Context) context.Context { 27 | return ctx 28 | } 29 | 30 | //nolint:unused // Linter is confused about this one. 31 | func (s *Suite) setContext(ctx context.Context) { 32 | s.ctx = ctx 33 | } 34 | 35 | func (s *Suite) Context() context.Context { 36 | return WithT(s.ctx, s.T()) 37 | } 38 | 39 | func (s *Suite) Assert() *Assertions { 40 | return &Assertions{Assertions: s.Suite.Assert()} 41 | } 42 | 43 | func (s *Suite) Require() *Requirements { 44 | return &Requirements{Assertions: s.Suite.Require()} 45 | } 46 | -------------------------------------------------------------------------------- /integration_test/itest/tempdir.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "sync/atomic" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | type tempDirBase struct { 13 | tempDir string 14 | tempDirSeq uint64 15 | } 16 | 17 | type tempDirBaseKey struct{} 18 | 19 | func withTempDirBase(ctx context.Context, td *tempDirBase) context.Context { 20 | return context.WithValue(ctx, tempDirBaseKey{}, td) 21 | } 22 | 23 | // TempDir returns a temporary directory for the test to use. 24 | // The directory is automatically removed when the test and 25 | // all its subtests complete. 26 | // Each subsequent call to t.TempDir returns a unique directory; 27 | // if the directory creation fails, TempDir terminates the test by calling Fatal. 28 | func TempDir(ctx context.Context) string { 29 | t := getT(ctx) 30 | if td, ok := ctx.Value(tempDirBaseKey{}).(*tempDirBase); ok { 31 | seq := atomic.AddUint64(&td.tempDirSeq, 1) 32 | dir := fmt.Sprintf("%s%c%03d", td.tempDir, os.PathSeparator, seq) 33 | require.NoError(t, os.Mkdir(dir, 0o777)) 34 | return dir 35 | } 36 | return t.TempDir() 37 | } 38 | -------------------------------------------------------------------------------- /integration_test/itest/timed.go: -------------------------------------------------------------------------------- 1 | package itest 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // TimedRun gives the given function maxDuration time to finish, and returns a timeout error if it doesn't. Otherwise, 9 | // it returns the error from the function. 10 | func TimedRun(ctx context.Context, maxDuration time.Duration, function func(ctx2 context.Context) error) error { 11 | ctx, cancel := context.WithTimeout(ctx, maxDuration) 12 | defer cancel() 13 | select { 14 | case <-ctx.Done(): 15 | return ctx.Err() 16 | default: 17 | return function(ctx) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /integration_test/list_watch_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 8 | ) 9 | 10 | func (s *connectedSuite) Test_ListWatch() { 11 | svc := "echo-easy" 12 | 13 | s.Run("-C", func() { 14 | // Use a context to end tele list -w 15 | ctx := s.Context() 16 | cancelctx, cancel := context.WithCancel(ctx) 17 | ch := make(chan string) 18 | go func() { 19 | stdout, _, _ := itest.Telepresence(cancelctx, "list", "--output", "json-stream") 20 | ch <- stdout 21 | }() 22 | time.Sleep(time.Second) 23 | s.ApplyApp(ctx, svc, "deploy/"+svc) 24 | defer s.DeleteSvcAndWorkload(ctx, "deploy", svc) 25 | time.Sleep(time.Second) 26 | cancel() 27 | s.Contains(<-ch, svc) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /integration_test/loglevel_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "bufio" 5 | "os" 6 | "path/filepath" 7 | "regexp" 8 | 9 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 10 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 11 | ) 12 | 13 | func (s *notConnectedSuite) Test_RootDaemonLogLevel() { 14 | require := s.Require() 15 | ctx := s.Context() 16 | s.TelepresenceConnect(ctx) 17 | itest.TelepresenceQuitOk(ctx) 18 | rootLogName := filepath.Join(filelocation.AppUserLogDir(ctx), "daemon.log") 19 | rootLog, err := os.Open(rootLogName) 20 | require.NoError(err) 21 | defer rootLog.Close() 22 | 23 | hasDebug := false 24 | scn := bufio.NewScanner(rootLog) 25 | match := regexp.MustCompile(` debug +daemon/server`) 26 | for scn.Scan() && !hasDebug { 27 | hasDebug = match.MatchString(scn.Text()) 28 | } 29 | s.True(hasDebug, "daemon.log does not contain expected debug statements") 30 | } 31 | -------------------------------------------------------------------------------- /integration_test/single_service_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "github.com/telepresenceio/telepresence/v2/integration_test/itest" 5 | ) 6 | 7 | type singleServiceSuite struct { 8 | itest.Suite 9 | itest.SingleService 10 | } 11 | 12 | func (s *singleServiceSuite) SuiteName() string { 13 | return "SingleService" 14 | } 15 | 16 | func init() { 17 | itest.AddSingleServiceSuite("", "echo", func(h itest.SingleService) itest.TestingSuite { 18 | return &singleServiceSuite{Suite: itest.Suite{Harness: h}, SingleService: h} 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /integration_test/subdomain_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | func (s *connectedSuite) Test_PodWithSubdomain() { 12 | c := s.Context() 13 | s.ApplyApp(c, "echo-w-subdomain", "deploy/echo-subsonic") 14 | defer func() { 15 | s.NoError(s.Kubectl(c, "delete", "svc", "subsonic")) 16 | s.NoError(s.Kubectl(c, "delete", "deploy", "echo-subsonic")) 17 | }() 18 | 19 | lookupHost := func(host string) { 20 | var err error 21 | s.Eventually(func() bool { 22 | c, cancel := context.WithTimeout(c, 1800*time.Millisecond) 23 | defer cancel() 24 | dlog.Info(c, "LookupHost("+host+")") 25 | _, err = net.DefaultResolver.LookupHost(c, host) 26 | return err == nil 27 | }, 10*time.Second, 2*time.Second, "%s did not resolve: %v", host, err) 28 | } 29 | lookupHost("echo.subsonic." + s.AppNamespace()) 30 | lookupHost("echo.subsonic." + s.AppNamespace() + ".svc.cluster.local") 31 | } 32 | -------------------------------------------------------------------------------- /integration_test/svcdomain_test.go: -------------------------------------------------------------------------------- 1 | package integration_test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "time" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | func (s *connectedSuite) Test_SvcDomain() { 13 | c := s.Context() 14 | s.ApplyEchoService(c, "echo", 8080) 15 | defer s.DeleteSvcAndWorkload(c, "deploy", "echo") 16 | 17 | host := fmt.Sprintf("echo.%s.svc", s.AppNamespace()) 18 | s.Eventually(func() bool { 19 | c, cancel := context.WithTimeout(c, 1800*time.Millisecond) 20 | defer cancel() 21 | dlog.Info(c, "LookupHost("+host+")") 22 | _, err := net.DefaultResolver.LookupHost(c, host) 23 | return s.NoErrorf(err, "%s did not resolve", host) 24 | }, 10*time.Second, 2*time.Second) 25 | } 26 | -------------------------------------------------------------------------------- /integration_test/testdata/cli-container/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM telepresence 2 | 3 | RUN apk add --no-cache bash curl 4 | 5 | ENTRYPOINT ["/bin/bash"] -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS base 2 | 3 | # Build Delve 4 | RUN go install github.com/go-delve/delve/cmd/dlv@latest 5 | 6 | WORKDIR /echo-server 7 | COPY go.mod . 8 | COPY go.sum . 9 | # Get dependencies - will also be cached if we won't change mod/sum 10 | RUN go mod download 11 | 12 | COPY frontend.go . 13 | COPY main.go . 14 | 15 | FROM base AS builder 16 | RUN go build -o echo-server . 17 | 18 | FROM base AS development 19 | RUN go build -gcflags="all=-N -l" -o echo-server . 20 | EXPOSE 40000 21 | CMD ["/go/bin/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/echo-server/echo-server"] 22 | 23 | FROM alpine AS production 24 | COPY --from=builder /echo-server/echo-server / 25 | CMD ["/echo-server"] 26 | -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/Dockerfile.debug: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | # Build Delve 4 | RUN go install github.com/go-delve/delve/cmd/dlv@latest 5 | 6 | WORKDIR /echo-server 7 | COPY go.mod . 8 | COPY go.sum . 9 | # Get dependencies - will also be cached if we won't change mod/sum 10 | RUN go mod download 11 | 12 | COPY frontend.go . 13 | COPY main.go . 14 | RUN go build -gcflags="all=-N -l" -o echo-server . 15 | 16 | EXPOSE 40000 17 | CMD ["/go/bin/dlv", "--listen=:40000", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "/echo-server/echo-server"] -------------------------------------------------------------------------------- /integration_test/testdata/echo-server/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.0 6 | 7 | require ( 8 | github.com/datawire/dlib v1.3.1 9 | github.com/gorilla/websocket v1.5.3 10 | golang.org/x/net v0.40.0 11 | ) 12 | 13 | require ( 14 | github.com/pkg/errors v0.9.1 // indirect 15 | github.com/sirupsen/logrus v1.9.3 // indirect 16 | golang.org/x/sys v0.33.0 // indirect 17 | golang.org/x/text v0.25.0 // indirect 18 | ) 19 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_experiment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: "echo-easy" 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | app: echo-easy 11 | ports: 12 | - name: proxied 13 | port: 80 14 | targetPort: http 15 | --- 16 | apiVersion: v1 17 | kind: ServiceAccount 18 | metadata: 19 | name: telepresence-test-developer 20 | 21 | --- 22 | kind: Role 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | metadata: 25 | name: traffic-manager-connect 26 | rules: 27 | - apiGroups: [""] 28 | resources: ["services"] 29 | verbs: ["get", "list"] 30 | 31 | --- 32 | apiVersion: rbac.authorization.k8s.io/v1 33 | kind: RoleBinding 34 | metadata: 35 | name: traffic-manager-connect 36 | subjects: 37 | - kind: ServiceAccount 38 | name: telepresence-test-developer 39 | roleRef: 40 | apiGroup: rbac.authorization.k8s.io 41 | name: traffic-manager-connect 42 | kind: Role 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_rancher.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | kind: ClusterRole 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: rancher-inspect 6 | labels: 7 | purpose: tp-cli-testing 8 | rules: 9 | - apiGroups: [""] 10 | resources: ["nodes"] 11 | verbs: ["get", "list"] 12 | 13 | --- 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | kind: ClusterRoleBinding 16 | metadata: 17 | name: rancher-inspect-{{ .ManagerNamespace }} 18 | labels: 19 | purpose: tp-cli-testing 20 | subjects: 21 | - kind: ServiceAccount 22 | name: telepresence-test-developer 23 | namespace: {{ .ManagerNamespace }} 24 | roleRef: 25 | apiGroup: rbac.authorization.k8s.io 26 | name: rancher-inspect 27 | kind: ClusterRole 28 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/client_sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: telepresence-test-developer 5 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/disruption-budget.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.DisruptionBudget*/ -}}--- 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{.Name}} 6 | spec: 7 | {{- if gt .MinAvailable 0 }} 8 | minAvailable: {{ .MinAvailable }} 9 | {{- end }} 10 | {{- if gt .MaxUnavailable 0 }} 11 | maxUnavailable: {{ .MaxUnavailable }} 12 | {{- end }} 13 | selector: 14 | matchLabels: 15 | budget: {{.Name}} 16 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-auto-inject.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-auto-inject 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-auto-inject 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-auto-inject 19 | labels: 20 | app: echo-auto-inject 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-auto-inject 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: enabled 30 | labels: 31 | app: echo-auto-inject 32 | spec: 33 | containers: 34 | - name: echo-auto-inject 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-double-one-unnamed.yaml: -------------------------------------------------------------------------------- 1 | # The echo-double-unnamed deployment exposes two unnamed ports, 8080 and 8081 from a single container 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: echo-double-one-unnamed 6 | labels: 7 | app: echo-double-one-unnamed 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: echo-double-one-unnamed 13 | template: 14 | metadata: 15 | annotations: 16 | telepresence.io/inject-container-ports: all 17 | labels: 18 | app: echo-double-one-unnamed 19 | spec: 20 | containers: 21 | - name: echo-server 22 | image: ghcr.io/telepresenceio/echo-server:latest 23 | imagePullPolicy: IfNotPresent 24 | ports: 25 | - containerPort: 8080 26 | - containerPort: 8081 27 | env: 28 | - name: PORTS 29 | value: "8080,8081" 30 | resources: 31 | limits: 32 | cpu: 50m 33 | memory: 8Mi 34 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-easy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-easy" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-easy 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-easy" 19 | labels: 20 | app: echo-easy 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-easy 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-easy 30 | spec: 31 | containers: 32 | - name: echo-easy 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | app: echo-headless 11 | ports: 12 | - name: http 13 | port: 8080 14 | targetPort: 8080 15 | --- 16 | apiVersion: apps/v1 17 | kind: StatefulSet 18 | metadata: 19 | name: echo-headless 20 | labels: 21 | app: echo-headless 22 | spec: 23 | replicas: 1 24 | serviceName: echo-headless 25 | selector: 26 | matchLabels: 27 | app: echo-headless 28 | template: 29 | metadata: 30 | labels: 31 | app: echo-headless 32 | spec: 33 | containers: 34 | - name: echo-headless 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-manual-inject-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: "manual-inject" 5 | labels: 6 | app: manual-inject 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: manual-inject 12 | template: 13 | metadata: 14 | labels: 15 | app: manual-inject 16 | spec: 17 | containers: 18 | - name: echo-container 19 | image: ghcr.io/telepresenceio/echo-server:latest 20 | imagePullPolicy: IfNotPresent 21 | ports: 22 | - containerPort: 80 23 | resources: {} 24 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-manual-inject-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: "manual-inject" 5 | spec: 6 | type: ClusterIP 7 | selector: 8 | app: manual-inject 9 | ports: 10 | - port: 80 11 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-min.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: policy/v1 16 | kind: PodDisruptionBudget 17 | metadata: 18 | name: echo 19 | spec: 20 | minAvailable: 1 21 | selector: 22 | matchLabels: 23 | app: echo 24 | --- 25 | apiVersion: apps/v1 26 | kind: Deployment 27 | metadata: 28 | name: "echo" 29 | labels: 30 | app: echo 31 | spec: 32 | replicas: 1 33 | selector: 34 | matchLabels: 35 | app: echo 36 | template: 37 | metadata: 38 | labels: 39 | app: echo 40 | spec: 41 | containers: 42 | - name: echo 43 | image: ghcr.io/telepresenceio/echo-server:latest 44 | imagePullPolicy: IfNotPresent 45 | ports: 46 | - containerPort: 8080 47 | name: http 48 | resources: 49 | limits: 50 | cpu: 50m 51 | memory: 128Mi 52 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-containerport.yaml: -------------------------------------------------------------------------------- 1 | # The echo-no-containerport deployment doesn't expose any ports but will reply on port 8080 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: echo-no-containerport 6 | labels: 7 | app: echo-no-containerport 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: echo-no-containerport 13 | template: 14 | metadata: 15 | labels: 16 | app: echo-no-containerport 17 | spec: 18 | containers: 19 | - name: echo-server 20 | image: ghcr.io/telepresenceio/echo-server:latest 21 | imagePullPolicy: IfNotPresent 22 | env: 23 | - name: PORTS 24 | value: "8080,8081" 25 | resources: 26 | limits: 27 | cpu: 50m 28 | memory: 8Mi 29 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-svc-ann.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: echo-no-svc-ann 5 | labels: 6 | app: echo-no-svc-ann 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: echo-no-svc-ann 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-no-svc-ann 16 | budget: telepresence-test 17 | annotations: 18 | telepresence.io/inject-container-ports: http 19 | spec: 20 | automountServiceAccountToken: false 21 | containers: 22 | - name: echo-server 23 | image: ghcr.io/telepresenceio/echo-server:latest 24 | imagePullPolicy: IfNotPresent 25 | ports: 26 | - name: http 27 | containerPort: 8080 28 | env: 29 | - name: PORT 30 | value: "8080" 31 | resources: 32 | limits: 33 | cpu: 50m 34 | memory: 8Mi 35 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: echo-no-svc 5 | labels: 6 | app: echo-no-svc 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: echo-no-svc 12 | template: 13 | metadata: 14 | labels: 15 | app: echo-no-svc 16 | spec: 17 | automountServiceAccountToken: false 18 | containers: 19 | - name: echo-server 20 | image: ghcr.io/telepresenceio/echo-server:latest 21 | imagePullPolicy: IfNotPresent 22 | ports: 23 | - name: http 24 | containerPort: 8080 25 | env: 26 | - name: PORT 27 | value: "8080" 28 | resources: 29 | limits: 30 | cpu: 50m 31 | memory: 8Mi 32 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-no-vols.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-no-vols 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-no-vols 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-no-vols 19 | labels: 20 | app: echo-no-vols 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-no-vols 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-no-vols 30 | budget: telepresence-test 31 | spec: 32 | automountServiceAccountToken: false 33 | containers: 34 | - name: echo-server 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - name: http 39 | containerPort: 8080 40 | env: 41 | - name: PORT 42 | value: "8080" 43 | resources: 44 | limits: 45 | cpu: 50m 46 | memory: 8Mi 47 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-one.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-one" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-one 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-one" 19 | labels: 20 | app: echo-one 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-one 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-one 30 | spec: 31 | containers: 32 | - name: echo-one 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-same-target-port.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-stp 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-stp 10 | ports: 11 | - name: eighty 12 | port: 80 13 | targetPort: 8080 14 | - name: eighty-eighty 15 | port: 8080 16 | targetPort: 8080 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: echo-stp 22 | labels: 23 | app: echo-stp 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: echo-stp 29 | template: 30 | metadata: 31 | labels: 32 | app: echo-stp 33 | spec: 34 | containers: 35 | - name: echo-stp 36 | image: ghcr.io/telepresenceio/echo-server:latest 37 | imagePullPolicy: IfNotPresent 38 | ports: 39 | - containerPort: 8080 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-udp-tcp-unnamed.yaml: -------------------------------------------------------------------------------- 1 | # The echo-double-unnamed deployment exposes two unnamed ports, 8080 and 8081 from a single container 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: echo-udp-tcp-unnamed 6 | labels: 7 | app: echo-udp-tcp-unnamed 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: echo-udp-tcp-unnamed 13 | template: 14 | metadata: 15 | labels: 16 | app: echo-udp-tcp-unnamed 17 | spec: 18 | containers: 19 | - name: echo-udp-server 20 | image: ghcr.io/telepresenceio/udp-echo:latest 21 | imagePullPolicy: IfNotPresent 22 | ports: 23 | - containerPort: 8080 24 | protocol: UDP 25 | env: 26 | - name: PORT 27 | value: "8080" 28 | resources: 29 | limits: 30 | cpu: 50m 31 | memory: 8Mi 32 | - name: echo-server 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | env: 38 | - name: PORT 39 | value: "8080" 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 8Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-w-hostalias.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo 19 | labels: 20 | app: echo 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo 26 | template: 27 | metadata: 28 | labels: 29 | app: echo 30 | spec: 31 | hostAliases: 32 | - ip: "{{ .AliasIP }}" 33 | hostnames: 34 | {{- range .Aliases }} 35 | - {{ . }} 36 | {{- end }} 37 | containers: 38 | - name: echo 39 | image: ghcr.io/telepresenceio/echo-server:latest 40 | imagePullPolicy: IfNotPresent 41 | ports: 42 | - containerPort: 8080 43 | name: http 44 | resources: 45 | limits: 46 | cpu: 50m 47 | memory: 128Mi 48 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo-w-subdomain.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: subsonic 6 | spec: 7 | selector: 8 | app: subsonic 9 | clusterIP: None 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: echo-subsonic 19 | labels: 20 | app: subsonic 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: subsonic 26 | template: 27 | metadata: 28 | labels: 29 | app: subsonic 30 | spec: 31 | hostname: echo 32 | subdomain: subsonic 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/echo_with_env.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-easy" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: echo-easy 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-easy" 19 | labels: 20 | app: echo-easy 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: echo-easy 26 | template: 27 | metadata: 28 | labels: 29 | app: echo-easy 30 | spec: 31 | containers: 32 | - name: echo-easy 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | env: 36 | - name: TEST 37 | value: "DATA" 38 | - name: INTERCEPT 39 | value: "ENV" 40 | - name: DATABASE_HOST 41 | value: "HOST_NAME" 42 | - name: DATABASE_PASSWORD 43 | value: "SUPER_SECRET_PASSWORD" 44 | ports: 45 | - containerPort: 8080 46 | name: http 47 | resources: 48 | limits: 49 | cpu: 50m 50 | memory: 128Mi 51 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/memory-constraints.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: LimitRange 3 | metadata: 4 | name: mem-min-max-test 5 | spec: 6 | limits: 7 | - max: 8 | memory: 100Mi 9 | min: 10 | memory: 20Mi 11 | type: Container 12 | 13 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/namespaces.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: alpha 6 | labels: 7 | phase: a1 8 | scope: dev 9 | --- 10 | apiVersion: v1 11 | kind: Namespace 12 | metadata: 13 | name: beta 14 | labels: 15 | phase: a1 16 | scope: dev 17 | --- 18 | apiVersion: v1 19 | kind: Namespace 20 | metadata: 21 | name: alpha-test 22 | labels: 23 | phase: none 24 | scope: unscoped 25 | --- 26 | apiVersion: v1 27 | kind: Namespace 28 | metadata: 29 | name: alpha-rc 30 | labels: 31 | phase: a2 32 | scope: dev 33 | --- 34 | apiVersion: v1 35 | kind: Namespace 36 | metadata: 37 | name: beta-rc 38 | labels: 39 | phase: a2 40 | scope: dev 41 | --- 42 | apiVersion: v1 43 | kind: Namespace 44 | metadata: 45 | name: alpha-live 46 | labels: 47 | phase: ga 48 | scope: prod 49 | --- 50 | apiVersion: v1 51 | kind: Namespace 52 | metadata: 53 | name: beta-live 54 | labels: 55 | phase: ga 56 | scope: prod 57 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-disabled.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-disabled 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-disabled 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-disabled 19 | labels: 20 | app: pol-disabled 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-disabled 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: disabled 30 | labels: 31 | app: pol-disabled 32 | spec: 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-enabled.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-enabled 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-enabled 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-enabled 19 | labels: 20 | app: pol-enabled 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-enabled 26 | template: 27 | metadata: 28 | annotations: 29 | telepresence.io/inject-traffic-agent: enabled 30 | labels: 31 | app: pol-enabled 32 | spec: 33 | containers: 34 | - name: echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pol-none.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: pol-none 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: pol-none 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: pol-none 19 | labels: 20 | app: pol-none 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: pol-none 26 | template: 27 | metadata: 28 | labels: 29 | app: pol-none 30 | spec: 31 | containers: 32 | - name: echo 33 | image: ghcr.io/telepresenceio/echo-server:latest 34 | imagePullPolicy: IfNotPresent 35 | ports: 36 | - containerPort: 8080 37 | name: http 38 | resources: 39 | limits: 40 | cpu: 50m 41 | memory: 128Mi 42 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pv.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.PersistentVolume*/ -}} 2 | apiVersion: v1 3 | kind: PersistentVolume 4 | metadata: 5 | name: {{ .Name }} 6 | labels: 7 | purpose: tp-cli-testing 8 | {{- with .Annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | nodeAffinity: 14 | required: 15 | nodeSelectorTerms: 16 | - matchExpressions: 17 | - key: kubernetes.io/os 18 | operator: In 19 | values: 20 | - linux 21 | capacity: 22 | storage: 0.1Gi 23 | accessModes: 24 | - ReadWriteMany 25 | persistentVolumeReclaimPolicy: Retain 26 | hostPath: 27 | path: /tmp/{{ .Name }}-pv 28 | {{- with .StorageClassName }} 29 | storageClassName: {{ . }} 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/pvc.goyaml: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/v2/integration_test/itest.PersistentVolumeClaim*/ -}} 2 | apiVersion: v1 3 | kind: PersistentVolumeClaim 4 | metadata: 5 | name: {{ .Name }} 6 | labels: 7 | purpose: tp-cli-testing 8 | {{- with .Annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | spec: 13 | accessModes: 14 | - ReadWriteMany 15 | resources: 16 | requests: 17 | storage: 0.1Gi 18 | {{- with .StorageClassName }} 19 | storageClassName: {{ . }} 20 | {{- end }} 21 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/rs-echo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: rs-echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: rs-echo 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: ReplicaSet 17 | metadata: 18 | name: rs-echo 19 | labels: 20 | app: rs-echo 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: rs-echo 26 | template: 27 | metadata: 28 | labels: 29 | app: rs-echo 30 | budget: telepresence-test 31 | spec: 32 | containers: 33 | - name: rs-echo 34 | image: ghcr.io/telepresenceio/echo-server:latest 35 | imagePullPolicy: IfNotPresent 36 | ports: 37 | - name: http 38 | containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/secret-reader-rbac.goyaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: secret-reader 6 | rules: 7 | - apiGroups: [""] 8 | resources: ["secrets"] 9 | verbs: ["get", "watch", "list"] 10 | --- 11 | apiVersion: rbac.authorization.k8s.io/v1 12 | kind: RoleBinding 13 | metadata: 14 | name: read-secrets 15 | subjects: 16 | - kind: ServiceAccount 17 | name: {{ .Name }} 18 | apiGroup: "" 19 | roleRef: 20 | kind: Role 21 | name: secret-reader 22 | apiGroup: rbac.authorization.k8s.io 23 | -------------------------------------------------------------------------------- /integration_test/testdata/k8s/ss-echo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: ss-echo 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | app: ss-echo 10 | ports: 11 | - name: http 12 | port: 80 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: ss-echo 19 | labels: 20 | app: ss-echo 21 | spec: 22 | serviceName: "ss-echo" 23 | replicas: 2 24 | selector: 25 | matchLabels: 26 | app: ss-echo 27 | template: 28 | metadata: 29 | labels: 30 | app: ss-echo 31 | budget: telepresence-test 32 | spec: 33 | containers: 34 | - name: ss-echo 35 | image: ghcr.io/telepresenceio/echo-server:latest 36 | imagePullPolicy: IfNotPresent 37 | ports: 38 | - containerPort: 8080 39 | resources: 40 | limits: 41 | cpu: 50m 42 | memory: 128Mi 43 | -------------------------------------------------------------------------------- /integration_test/testdata/routing-values.yaml: -------------------------------------------------------------------------------- 1 | client: 2 | routing: 3 | # add the following subnets to the client's virtual network interface 4 | # array of strings 5 | alsoProxySubnets: ["11.11.11.11/32", "12.12.12.12/32"] 6 | neverProxySubnets: ["13.11.11.11/32", "14.12.12.12/32"] -------------------------------------------------------------------------------- /integration_test/testdata/scripts/veth-down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ip link set vm2 down 3 | ip link set brm down 4 | 5 | for var in "$@" 6 | do 7 | ip addr del "$(echo "$var" | sed -r 's/\.0$/.1/')" dev brm 8 | ip addr del "$(echo "$var" | sed -r 's/\.0$/.2/')" dev vm2 9 | done 10 | 11 | ip link del brm type bridge 12 | ip link set dev tapm down 13 | ip tuntap del tapm mode tap 14 | ip link set dev vm1 down 15 | ip link del dev vm1 type veth peer name vm2 16 | -------------------------------------------------------------------------------- /integration_test/testdata/scripts/veth-up.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | ip link add dev vm1 type veth peer name vm2 3 | ip link set dev vm1 up 4 | ip tuntap add tapm mode tap 5 | ip link set dev tapm up 6 | ip link add brm type bridge 7 | 8 | ip link set tapm master brm 9 | ip link set vm1 master brm 10 | 11 | for var in "$@" 12 | do 13 | ip addr add "$(echo "$var" | sed -r 's/\.0$/.1/')" dev brm 14 | ip addr add "$(echo "$var" | sed -r 's/\.0$/.2/')" dev vm2 15 | done 16 | 17 | ip link set brm up 18 | ip link set vm2 up -------------------------------------------------------------------------------- /integration_test/testdata/telepresence-1.9.9.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/integration_test/testdata/telepresence-1.9.9.tgz -------------------------------------------------------------------------------- /integration_test/testdata/udp-echo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine3.15 AS builder 2 | 3 | WORKDIR /udp-echo 4 | COPY go.mod . 5 | COPY main.go . 6 | RUN go build -o udp-echo . 7 | 8 | FROM alpine:3.15 9 | COPY --from=builder /udp-echo/udp-echo /usr/local/bin/ 10 | ENTRYPOINT ["/usr/local/bin/udp-echo"] 11 | -------------------------------------------------------------------------------- /integration_test/testdata/udp-echo/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /k8s/agent-injector-rbac.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: traffic-manager 6 | namespace: ambassador 7 | --- 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: traffic-manager 12 | rules: 13 | - apiGroups: 14 | - "" 15 | resources: ["services"] 16 | verbs: ["get", "list"] 17 | --- 18 | apiVersion: rbac.authorization.k8s.io/v1 19 | kind: ClusterRoleBinding 20 | metadata: 21 | name: traffic-manager 22 | subjects: 23 | - kind: ServiceAccount 24 | name: traffic-manager 25 | namespace: ambassador 26 | roleRef: 27 | kind: ClusterRole 28 | name: traffic-manager 29 | apiGroup: rbac.authorization.k8s.io 30 | -------------------------------------------------------------------------------- /k8s/apitest.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "apitest" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | service: apitest 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "apitest" 19 | labels: 20 | service: apitest 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | service: apitest 26 | template: 27 | metadata: 28 | labels: 29 | service: apitest 30 | spec: 31 | containers: 32 | - name: apitest 33 | image: localhost:5000/apiserveraccess:latest 34 | ports: 35 | - containerPort: 8080 36 | name: http 37 | env: 38 | - name: APP_PORT 39 | value: "8080" 40 | - name: LOG_LEVEL 41 | value: "DEBUG" 42 | resources: 43 | limits: 44 | cpu: 50m 45 | memory: 128Mi 46 | -------------------------------------------------------------------------------- /k8s/dnsutils-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: dnsutils-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | service: dnsutils-headless 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: dnsutils-headless 19 | labels: 20 | service: dnsutils-headless 21 | spec: 22 | replicas: 1 23 | serviceName: dnsutils-headless 24 | selector: 25 | matchLabels: 26 | service: dnsutils-headless 27 | template: 28 | metadata: 29 | labels: 30 | service: dnsutils-headless 31 | spec: 32 | containers: 33 | - name: dnsutils-headless 34 | image: gcr.io/kubernetes-e2e-test-images/dnsutils:1.3 35 | command: 36 | - sleep 37 | - "3600" 38 | imagePullPolicy: IfNotPresent 39 | restartPolicy: Always 40 | -------------------------------------------------------------------------------- /k8s/echo-auto-headless.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: echo-auto-headless 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | service: echo-auto-headless 11 | ports: 12 | - port: 8080 13 | targetPort: 8080 14 | --- 15 | apiVersion: apps/v1 16 | kind: StatefulSet 17 | metadata: 18 | name: echo-auto-headless 19 | labels: 20 | service: echo-auto-headless 21 | spec: 22 | replicas: 1 23 | serviceName: echo-auto-headless 24 | selector: 25 | matchLabels: 26 | service: echo-auto-headless 27 | template: 28 | metadata: 29 | labels: 30 | service: echo-auto-headless 31 | annotations: 32 | telepresence.io/inject-traffic-agent: enabled 33 | telepresence.io/inject-service-ports: "8080" 34 | spec: 35 | containers: 36 | - name: echo-auto-headless 37 | image: jmalloc/echo-server 38 | ports: 39 | - containerPort: 8080 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | 45 | -------------------------------------------------------------------------------- /k8s/echo-double-one.yaml: -------------------------------------------------------------------------------- 1 | # The echo-double-one service exposes two ports, 80 and 81 and directs them to one single container 2 | --- 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | name: "echo-double-one" 7 | spec: 8 | type: ClusterIP 9 | selector: 10 | service: echo-double-one 11 | ports: 12 | - name: http 13 | port: 80 14 | targetPort: http 15 | - name: extra 16 | port: 81 17 | targetPort: extra 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: "echo-double-one" 23 | labels: 24 | service: echo-double-one 25 | spec: 26 | replicas: 1 27 | selector: 28 | matchLabels: 29 | service: echo-double-one 30 | template: 31 | metadata: 32 | labels: 33 | service: echo-double-one 34 | spec: 35 | containers: 36 | - name: echo-double 37 | image: multi:5000/tel2/echo 38 | ports: 39 | - containerPort: 8080 40 | name: http 41 | - containerPort: 8081 42 | name: extra 43 | env: 44 | - name: PORTS 45 | value: "8080,8081" 46 | resources: 47 | limits: 48 | cpu: 50m 49 | memory: 128Mi 50 | -------------------------------------------------------------------------------- /k8s/echo-sc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: "echo-sc" 6 | spec: 7 | type: ClusterIP 8 | selector: 9 | service: echo-sc 10 | ports: 11 | - name: proxied 12 | port: 80 13 | targetPort: http 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: "echo-sc" 19 | labels: 20 | service: echo-sc 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | service: echo-sc 26 | template: 27 | metadata: 28 | labels: 29 | service: echo-sc 30 | spec: 31 | securityContext: 32 | fsGroup: 1000 33 | runAsUser: 1000 34 | containers: 35 | - name: echo-sc 36 | image: ghcr.io/telepresenceio/echo-server:latest 37 | ports: 38 | - containerPort: 8080 39 | name: http 40 | resources: 41 | limits: 42 | cpu: 50m 43 | memory: 128Mi 44 | -------------------------------------------------------------------------------- /k8s/ext-example.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: ext-example 6 | spec: 7 | type: ExternalName 8 | externalName: example.com 9 | -------------------------------------------------------------------------------- /k8s/manager.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: traffic-manager 6 | spec: 7 | type: ClusterIP 8 | clusterIP: None 9 | selector: 10 | app: traffic-manager 11 | telepresence: manager 12 | ports: 13 | - name: api 14 | port: 8081 15 | targetPort: api 16 | --- 17 | apiVersion: apps/v1 18 | kind: Deployment 19 | metadata: 20 | name: traffic-manager 21 | labels: 22 | app: traffic-manager 23 | telepresence: manager 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: traffic-manager 29 | telepresence: manager 30 | template: 31 | metadata: 32 | labels: 33 | app: traffic-manager 34 | telepresence: manager 35 | spec: 36 | containers: 37 | - name: traffic-manager 38 | image: ko://github.com/telepresenceio/telepresence/v2/cmd/traffic 39 | ports: 40 | - name: api 41 | containerPort: 8081 42 | restartPolicy: Always 43 | -------------------------------------------------------------------------------- /k8s/private-reg-proxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: DaemonSet 3 | metadata: 4 | name: private-registry-proxy 5 | labels: 6 | app: private-registry-proxy 7 | spec: 8 | selector: 9 | matchLabels: 10 | app: private-registry-proxy 11 | template: 12 | metadata: 13 | labels: 14 | app: private-registry-proxy 15 | spec: 16 | containers: 17 | - name: tcp-proxy 18 | image: quay.io/bentoml/proxy-to-service:v2 19 | args: 20 | - tcp 21 | - "5000" 22 | - docker-registry.default.svc.cluster.local 23 | ports: 24 | - containerPort: 5000 25 | hostPort: 5000 26 | name: tcp 27 | protocol: TCP 28 | -------------------------------------------------------------------------------- /k8s/rs-echo-svc2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use a second service here that appoints the same port in the replicaset to 3 | # make things a bit more complex for the discovery mechanism, and also force 4 | # use of the --service flag when intercepting 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: rs-echo-canary 9 | spec: 10 | type: ClusterIP 11 | selector: 12 | service: rs-echo 13 | ports: 14 | - name: http 15 | port: 80 16 | targetPort: 8080 17 | -------------------------------------------------------------------------------- /packaging/artifacthub-repo.yml: -------------------------------------------------------------------------------- 1 | repositoryID: 37605942-af09-4135-bd75-5241c570ad76 2 | owners: 3 | - name: Thomas Hallgren 4 | email: thomas@tada.se 5 | -------------------------------------------------------------------------------- /packaging/bundle.wxs.in: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packaging/helmpackage.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | 8 | "github.com/blang/semver/v4" 9 | 10 | telcharts "github.com/telepresenceio/telepresence/v2/charts" 11 | ) 12 | 13 | type sv semver.Version 14 | 15 | func (v *sv) String() string { 16 | return (*semver.Version)(v).String() 17 | } 18 | 19 | func (v *sv) Set(s string) error { 20 | ver, err := semver.Parse(s) 21 | if err == nil { 22 | *v = sv(ver) 23 | } 24 | return err 25 | } 26 | 27 | func main() { 28 | var output string 29 | var version sv 30 | flag.StringVar(&output, "o", "", "output file") 31 | flag.Var(&version, "v", "Helm chart version") 32 | flag.Parse() 33 | err := packageHelmChart(output, semver.Version(version)) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | } 38 | 39 | func packageHelmChart(filename string, version semver.Version) error { 40 | fh, err := os.Create(filename) 41 | if err != nil { 42 | return err 43 | } 44 | defer fh.Close() 45 | return telcharts.WriteChart(telcharts.DirTypeTelepresence, fh, telcharts.TelepresenceChartName, version.String()) 46 | } 47 | -------------------------------------------------------------------------------- /packaging/homebrew-oss-formula.rb: -------------------------------------------------------------------------------- 1 | # This script is generated automatically by the release automation code in the 2 | # Telepresence repository: 3 | class __FORMULA_NAME__ < Formula 4 | desc "Local dev environment attached to a remote Kubernetes cluster" 5 | homepage "https://telepresence.io" 6 | version "__NEW_VERSION__" 7 | 8 | BASE_URL = "https://github.com/telepresenceio/telepresence/releases/download" 9 | ARCH = Hardware::CPU.arm? ? "arm64" : "amd64" 10 | OPERATING_SYSTEM = OS.mac? ? "darwin" : "linux" 11 | PACKAGE_NAME = "telepresence-#{OPERATING_SYSTEM}-#{ARCH}" 12 | 13 | url "#{BASE_URL}/v#{version}/#{PACKAGE_NAME}" 14 | 15 | sha256 "__TARBALL_HASH_DARWIN_AMD64__" if OS.mac? && Hardware::CPU.intel? 16 | sha256 "__TARBALL_HASH_DARWIN_ARM64__" if OS.mac? && Hardware::CPU.arm? 17 | sha256 "__TARBALL_HASH_LINUX_AMD64__" if OS.linux? && Hardware::CPU.intel? 18 | # TODO support linux arm64 19 | #sha256 "__TARBALL_HASH_LINUX_ARM64__" if OS.linux? && Hardware::CPU.arm? 20 | 21 | conflicts_with "telepresence" 22 | 23 | def install 24 | bin.install "#{PACKAGE_NAME}" => "telepresence" 25 | end 26 | 27 | test do 28 | system "#{bin}/telepresence", "--help" 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /packaging/install-telepresence.ps1: -------------------------------------------------------------------------------- 1 | #Requires -RunAsAdministrator 2 | 3 | param 4 | ( 5 | $Path = "$env:ProgramFiles\telepresence" 6 | ) 7 | 8 | $current_directory = (Get-Location).path 9 | 10 | echo "Installing telepresence to $Path" 11 | 12 | Start-Process msiexec -Wait -verb runAs -Args "/i $current_directory\winfsp.msi /passive /qn /L*V winfsp-install.log" 13 | Start-Process msiexec -Wait -verb runAs -Args "/i $current_directory\sshfs-win.msi /passive /qn /L*V sshfs-win-install.log" 14 | 15 | if(!(test-path $Path)) 16 | { 17 | New-Item -ItemType Directory -Force -Path $Path 18 | } 19 | 20 | Copy-Item "telepresence.exe" -Destination "$Path" -Force 21 | Copy-Item "wintun.dll" -Destination "$Path" -Force 22 | 23 | # Update PATH if entries do not exist only 24 | $currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine") 25 | @("$Path", "C:\Program Files\SSHFS-Win\bin") | Where-Object { $currentPath -notlike "*$_*" } | ForEach-Object { $currentPath = "$_;$currentPath" } 26 | [Environment]::SetEnvironmentVariable("Path", $currentPath, "Machine") 27 | 28 | echo "Telepresence installed to $Path" -------------------------------------------------------------------------------- /packaging/sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telepresenceio/telepresence/e3df0690479a6e9a6532dd59a00138237775d55e/packaging/sidebar.png -------------------------------------------------------------------------------- /pkg/agentconfig/initcontainer.go: -------------------------------------------------------------------------------- 1 | package agentconfig 2 | 3 | import ( 4 | "fmt" 5 | 6 | core "k8s.io/api/core/v1" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/annotation" 9 | ) 10 | 11 | func InitContainer(config *Sidecar) *core.Container { 12 | ic := &core.Container{ 13 | Name: InitContainerName, 14 | Image: config.AgentImage, 15 | Args: []string{"agent-init"}, 16 | Env: []core.EnvVar{ 17 | { 18 | Name: "LOG_LEVEL", 19 | Value: config.LogLevel, 20 | }, 21 | { 22 | Name: "AGENT_CONFIG", 23 | ValueFrom: &core.EnvVarSource{ 24 | FieldRef: &core.ObjectFieldSelector{ 25 | APIVersion: "v1", 26 | FieldPath: fmt.Sprintf("metadata.annotations['%s']", annotation.Config), 27 | }, 28 | }, 29 | }, 30 | { 31 | Name: "POD_IP", 32 | ValueFrom: &core.EnvVarSource{ 33 | FieldRef: &core.ObjectFieldSelector{ 34 | APIVersion: "v1", 35 | FieldPath: "status.podIP", 36 | }, 37 | }, 38 | }, 39 | }, 40 | SecurityContext: &core.SecurityContext{ 41 | Capabilities: &core.Capabilities{ 42 | Add: []core.Capability{"NET_ADMIN"}, 43 | }, 44 | }, 45 | } 46 | if r := config.InitResources; r != nil { 47 | ic.Resources = *r 48 | } 49 | if s := config.InitSecurityContext; s != nil { 50 | ic.SecurityContext = s 51 | } 52 | return ic 53 | } 54 | -------------------------------------------------------------------------------- /pkg/agentmap/capsbase26.go: -------------------------------------------------------------------------------- 1 | package agentmap 2 | 3 | // CapsBase26 converts the given number into base 26, represented using the letters 'A' to 'Z'. 4 | func CapsBase26(v uint64) string { 5 | return addBase26('A', v) 6 | } 7 | 8 | // Base26 converts the given number into base 26, represented using the letters 'a' to 'z'. 9 | func Base26(v uint64) string { 10 | return addBase26('a', v) 11 | } 12 | 13 | // Base26 converts the given number into base 26 represented using the letters 'a' to 'z'. 14 | func addBase26(c byte, v uint64) string { 15 | i := 14 // covers v == math.MaxUint64 16 | b := make([]byte, i) 17 | for { 18 | l := v % 26 19 | i-- 20 | b[i] = c + byte(l) 21 | if v < 26 { 22 | break 23 | } 24 | v /= 26 25 | } 26 | return string(b[i:]) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/agentmap/capsbase26_test.go: -------------------------------------------------------------------------------- 1 | package agentmap 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCapsBase26(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | v uint64 14 | want string 15 | }{ 16 | { 17 | "zero", 18 | 0, 19 | "A", 20 | }, 21 | { 22 | "25", 23 | 25, 24 | "Z", 25 | }, 26 | { 27 | "26", 28 | 26, 29 | "BA", 30 | }, 31 | { 32 | "51", 33 | 26 + 25, 34 | "BZ", 35 | }, 36 | { 37 | "52", 38 | 2 * 26, 39 | "CA", 40 | }, 41 | { 42 | "1351", 43 | 2*26*26 - 1, 44 | "BZZ", 45 | }, 46 | { 47 | "1352", 48 | 2 * 26 * 26, 49 | "CAA", 50 | }, 51 | { 52 | "maxuint", 53 | math.MaxUint64, 54 | "HLHXCZMXSYUMQP", 55 | }, 56 | } 57 | for _, tt := range tests { 58 | t.Run(tt.name, func(t *testing.T) { 59 | assert.Equalf(t, tt.want, CapsBase26(tt.v), "CapsBase26(%v)", tt.v) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/authenticator/config.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import "k8s.io/client-go/tools/clientcmd" 4 | 5 | func LoadKubeConfig(kubeConfig string) clientcmd.ClientConfig { 6 | loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 7 | loadingRules.ExplicitPath = kubeConfig 8 | 9 | return clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) 10 | } 11 | -------------------------------------------------------------------------------- /pkg/authenticator/exec.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | 8 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 11 | "github.com/telepresenceio/telepresence/v2/pkg/proc" 12 | ) 13 | 14 | type execCredentialBinary struct{} 15 | 16 | func (e execCredentialBinary) Resolve( 17 | ctx context.Context, 18 | execConfig *clientcmdapi.ExecConfig, 19 | ) ([]byte, error) { 20 | var buf bytes.Buffer 21 | 22 | cmd := proc.CommandContext(ctx, execConfig.Command, execConfig.Args...) 23 | cmd.Stdout = &buf 24 | cmd.Stderr = dos.Stderr(ctx) 25 | cmd.DisableLogging = true 26 | cmd.Env = dos.Environ(ctx) 27 | if len(execConfig.Env) > 0 { 28 | em := dos.FromEnvPairs(cmd.Env) 29 | for _, ev := range execConfig.Env { 30 | em[ev.Name] = ev.Value 31 | } 32 | cmd.Env = em.Environ() 33 | } 34 | 35 | if err := cmd.Run(); err != nil { 36 | return nil, fmt.Errorf("failed to run host command: %w", err) 37 | } 38 | 39 | return buf.Bytes(), nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/authenticator/exec_test.go: -------------------------------------------------------------------------------- 1 | package authenticator 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 9 | ) 10 | 11 | func TestExecCredentialsNoLocalEnv(t *testing.T) { 12 | t.Setenv("GLOBAL_ENV", "global-val") 13 | 14 | config := &clientcmdapi.ExecConfig{ 15 | Command: "sh", 16 | Args: []string{"-c", "echo $GLOBAL_ENV/$LOCAL_ENV"}, 17 | } 18 | result, err := execCredentialBinary{}.Resolve(context.Background(), config) 19 | assert.NoError(t, err) 20 | assert.Equal(t, string(result), "global-val/\n") 21 | } 22 | 23 | func TestExecCredentialsYesLocalEnv(t *testing.T) { 24 | t.Setenv("GLOBAL_ENV", "global-val") 25 | 26 | config := &clientcmdapi.ExecConfig{ 27 | Command: "sh", 28 | Args: []string{"-c", "echo $GLOBAL_ENV/$LOCAL_ENV"}, 29 | Env: []clientcmdapi.ExecEnvVar{{Name: "LOCAL_ENV", Value: "local-val"}}, 30 | } 31 | result, err := execCredentialBinary{}.Resolve(context.Background(), config) 32 | assert.NoError(t, err) 33 | assert.Equal(t, string(result), "global-val/local-val\n") 34 | } 35 | -------------------------------------------------------------------------------- /pkg/client/bwcompat/clusterinfo.go: -------------------------------------------------------------------------------- 1 | package bwcompat 2 | 3 | import ( 4 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 5 | "github.com/telepresenceio/telepresence/v2/pkg/iputil" 6 | ) 7 | 8 | func FixLegacyClusterInfo(mgrInfo *manager.ClusterInfo) { 9 | if len(mgrInfo.ServiceCidrs) == 0 && mgrInfo.ServiceSubnet != nil { 10 | // Older client with no support for multiple service subnets 11 | cidr := iputil.RPCToPrefix(mgrInfo.ServiceSubnet) 12 | sb, _ := cidr.MarshalBinary() 13 | mgrInfo.ServiceCidrs = [][]byte{sb} 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pkg/client/cli/ann/annotations.go: -------------------------------------------------------------------------------- 1 | package ann 2 | 3 | // -- Annotation keys 4 | 5 | const ( 6 | UserDaemon = "userD" 7 | Session = "session" 8 | VersionCheck = "versionCheck" 9 | UpdateCheckFormat = "updateCheckFormat" 10 | Progress = "progress" 11 | ) 12 | 13 | // -- Annotation values 14 | 15 | const ( 16 | Optional = "optional" 17 | Required = "required" 18 | Tel2 = "https://%s/download/tel2/%s/%s/stable.txt" 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/connect.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "slices" 6 | 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 10 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/connect" 11 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/daemon" 12 | ) 13 | 14 | func connectCmd() *cobra.Command { 15 | var request *daemon.CobraRequest 16 | 17 | cmd := &cobra.Command{ 18 | Use: "connect [flags] [-- ]", 19 | Args: cobra.ArbitraryArgs, 20 | Short: "Connect to a cluster", 21 | Annotations: map[string]string{ 22 | ann.Session: ann.Required, 23 | }, 24 | RunE: func(cmd *cobra.Command, args []string) error { 25 | if err := request.CommitFlags(cmd); err != nil { 26 | return err 27 | } 28 | return connect.RunConnect(cmd, args) 29 | }, 30 | ValidArgsFunction: func(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { 31 | dir := cobra.ShellCompDirectiveNoFileComp 32 | if slices.Contains(os.Args, "--") { 33 | dir = cobra.ShellCompDirectiveDefault 34 | } 35 | return nil, dir 36 | }, 37 | } 38 | request = daemon.InitRequest(cmd) 39 | return cmd 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/curl.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "slices" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 9 | ) 10 | 11 | func curlCmd() *cobra.Command { 12 | cmd := &cobra.Command{ 13 | Use: "curl", 14 | Short: "curl with daemon network", 15 | Args: cobra.ArbitraryArgs, 16 | Annotations: map[string]string{ 17 | ann.Session: ann.Optional, 18 | }, 19 | RunE: runCurl, 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | SilenceErrors: true, 22 | SilenceUsage: true, 23 | DisableFlagParsing: true, 24 | DisableFlagsInUseLine: true, 25 | DisableSuggestions: true, 26 | } 27 | return cmd 28 | } 29 | 30 | func runCurl(cmd *cobra.Command, args []string) error { 31 | return runDockerRun(cmd, slices.Insert(args, 0, "--rm", "curlimages/curl")) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/ingest.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ingest" 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 9 | ) 10 | 11 | func ingestCmd() *cobra.Command { 12 | ic := &ingest.Command{} 13 | cmd := &cobra.Command{ 14 | Use: "ingest [flags] [-- [[docker run flags] ] OR []] args...]", 15 | Args: cobra.MinimumNArgs(1), 16 | Short: "Ingest a container", 17 | Annotations: map[string]string{ 18 | ann.Session: ann.Required, 19 | ann.UpdateCheckFormat: ann.Tel2, 20 | }, 21 | SilenceUsage: true, 22 | SilenceErrors: true, 23 | RunE: ic.Run, 24 | ValidArgsFunction: intercept.ValidArgs, // a list that this command shares with intercept 25 | } 26 | ic.AddFlags(cmd) 27 | return cmd 28 | } 29 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/intercept.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func interceptCmd() *cobra.Command { 11 | ic := &intercept.Command{} 12 | cmd := &cobra.Command{ 13 | Use: "intercept [flags] [-- [[docker run flags] ] OR []] args...]", 14 | Args: cobra.MinimumNArgs(1), 15 | Short: "Intercept a service", 16 | Annotations: map[string]string{ 17 | ann.Session: ann.Required, 18 | ann.UpdateCheckFormat: ann.Tel2, 19 | }, 20 | SilenceUsage: true, 21 | SilenceErrors: true, 22 | RunE: ic.Run, 23 | ValidArgsFunction: intercept.ValidArgs, 24 | } 25 | ic.AddInterceptFlags(cmd) 26 | return cmd 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/list_namespaces.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/daemon" 9 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/output" 10 | ) 11 | 12 | type listNamespacesCommand struct { 13 | rq *daemon.CobraRequest 14 | } 15 | 16 | func listNamespaces() *cobra.Command { 17 | lnc := &listNamespacesCommand{} 18 | 19 | cmd := &cobra.Command{ 20 | Use: "list-namespaces", 21 | Args: cobra.NoArgs, 22 | Short: "Show all namespaces", 23 | RunE: lnc.run, 24 | ValidArgsFunction: cobra.NoFileCompletions, 25 | } 26 | lnc.rq = daemon.InitRequest(cmd) 27 | return cmd 28 | } 29 | 30 | func (lnc *listNamespacesCommand) run(cmd *cobra.Command, _ []string) error { 31 | nss, err := lnc.rq.GetAllNamespaces(cmd) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | ctx := cmd.Context() 37 | if output.WantsFormatted(cmd) { 38 | output.Object(ctx, nss, false) 39 | } else { 40 | for _, ns := range nss { 41 | fmt.Fprintln(output.Out(ctx), ns) 42 | } 43 | } 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/quit.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/connect" 8 | ) 9 | 10 | func quit() *cobra.Command { 11 | quitDaemons := false 12 | cmd := &cobra.Command{ 13 | Use: "quit", 14 | Args: cobra.NoArgs, 15 | Short: "Tell telepresence daemons to quit", 16 | RunE: func(cmd *cobra.Command, _ []string) error { 17 | if quitDaemons { 18 | connect.InitProgressWriter(cmd) 19 | connect.Quit(cmd.Context()) 20 | } else { 21 | cmd.Annotations = map[string]string{ann.UserDaemon: ann.Optional} 22 | if err := connect.InitCommand(cmd); err != nil { 23 | return err 24 | } 25 | connect.Disconnect(cmd.Context()) 26 | } 27 | return nil 28 | }, 29 | ValidArgsFunction: cobra.NoFileCompletions, 30 | } 31 | flags := cmd.Flags() 32 | flags.BoolVarP(&quitDaemons, "stop-daemons", "s", false, "stop all local telepresence daemons") 33 | return cmd 34 | } 35 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/replace.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func replaceCmd() *cobra.Command { 11 | ic := &intercept.Command{} 12 | cmd := &cobra.Command{ 13 | Use: "replace [flags] [-- [[docker run flags] ] OR []] args...]", 14 | Args: cobra.MinimumNArgs(1), 15 | Short: "Replace a container", 16 | Annotations: map[string]string{ 17 | ann.Session: ann.Required, 18 | ann.UpdateCheckFormat: ann.Tel2, 19 | }, 20 | SilenceUsage: true, 21 | SilenceErrors: true, 22 | RunE: ic.RunReplace, 23 | ValidArgsFunction: intercept.ValidArgs, 24 | } 25 | ic.AddReplaceFlags(cmd) 26 | return cmd 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/license: -------------------------------------------------------------------------------- 1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 2 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/cli.log: -------------------------------------------------------------------------------- 1 | 2022-02-12 15:02:29.6927 info Logging at this level "debug" -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/connector-20210916T130356.log: -------------------------------------------------------------------------------- 1 | 2021-09-16 13:03:47.7084 info Logging at this level "info" 2 | 2021-09-16 13:03:47.7451 info --- 3 | 2021-09-16 13:03:47.7451 info Telepresence Connector v2.4.3-98-g0585bbd1-1631811285 (api v3) starting... 4 | 2021-09-16 13:03:47.7452 info PID is 42128 5 | 2021-09-16 13:03:47.7452 info 6 | 2021-09-16 13:03:47.7453 info connector/server-grpc : gRPC server started 7 | 2021-09-16 13:03:54.6001 info connector/background-systema:shutdown_logger : shutting down (gracefully)... 8 | 2021-09-16 13:03:54.6002 info connector:shutdown_logger : shutting down (gracefully)... 9 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/echo-auto-inject-6496f77cbd-n86nc: -------------------------------------------------------------------------------- 1 | time="2021-09-16 17:07:04.8111" level=info msg="Logging at this level \"info\"" 2 | time="2021-09-16 17:07:04.8112" level=info msg="Traffic Agent v2.4.2 [pid:1]" 3 | time="2021-09-16 17:07:04.8112" level=info msg="{Name:echo-auto-inject Namespace:default PodIP:10.42.0.25 AgentPort:9900 AppMounts:/tel_app_mounts AppPort:8080 ManagerHost:traffic-manager.ambassador ManagerPort:8081}" 4 | time="2021-09-16 17:07:04.8257" level=info msg="new agent secrets mount path: /var/run/secrets/kubernetes.io" 5 | time="2021-09-16 17:07:04.8298" level=info msg="Connected to Manager v2.4.3-98-g0585bbd1-1631811285" THREAD=/client 6 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/testLogDir/traffic-manager-5c69859f94-g4ntj: -------------------------------------------------------------------------------- 1 | 2021-09-16 17:06:51.8710 info Logging at this level "info" 2 | 2021-09-16 17:06:51.8711 info Traffic Manager v2.4.3-98-g0585bbd1-1631811285 [pid:1] 3 | 2021-09-16 17:06:51.9889 info Using DNS IP from kube-dns.kube-system 4 | 2021-09-16 17:06:51.9959 info Using cluster domain "cluster.local." 5 | 2021-09-16 17:06:52.0282 info Extracting service subnet 10.43.0.0/16 from create service error message 6 | 2021-09-16 17:06:52.0572 info Deriving subnets from podCIRs of nodes 7 | 2021-09-16 17:06:52.0852 info agent-injector : Mutating webhook service is listening on :8443 8 | 2021/09/16 17:06:52 Patching synced Node fe5953af-7777-4787-accf-719601327fb6 9 | 2021-09-16 17:07:03.4843 info agent-injector : Injecting traffic-agent into pod echo-auto-inject-6496f77cbd-.default 10 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/diff_name.log: -------------------------------------------------------------------------------- 1 | logs from diff_name.log 2 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/file1.log: -------------------------------------------------------------------------------- 1 | logs from file1.log 2 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/testdata/zipDir/file2.log: -------------------------------------------------------------------------------- 1 | logs from file2.log 2 | -------------------------------------------------------------------------------- /pkg/client/cli/cmd/wiretap.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/ann" 7 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/intercept" 8 | ) 9 | 10 | func wiretapCmd() *cobra.Command { 11 | ic := &intercept.Command{ 12 | Wiretap: true, 13 | } 14 | cmd := &cobra.Command{ 15 | Use: "wiretap [flags] [-- ]", 16 | Args: cobra.MinimumNArgs(1), 17 | Short: "Wiretap a Service", 18 | Annotations: map[string]string{ 19 | ann.Session: ann.Required, 20 | ann.UpdateCheckFormat: ann.Tel2, 21 | }, 22 | SilenceUsage: true, 23 | SilenceErrors: true, 24 | RunE: ic.Run, 25 | ValidArgsFunction: intercept.ValidArgs, 26 | } 27 | ic.AddInterceptFlags(cmd) 28 | return cmd 29 | } 30 | -------------------------------------------------------------------------------- /pkg/client/cli/docker/auto_complete.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "os/exec" 7 | "regexp" 8 | "slices" 9 | "strconv" 10 | "strings" 11 | 12 | "github.com/spf13/cobra" 13 | ) 14 | 15 | const Exe = "docker" 16 | 17 | var directiveCodeRx = regexp.MustCompile(`^:(\d)$`) //nolint:gochecknoglobals // constant 18 | 19 | func AutocompleteRun(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 20 | args = slices.Insert(args, 0, "__completeNoDesc", "run") 21 | args = append(args, toComplete) 22 | cc := exec.CommandContext(cmd.Context(), Exe, args...) 23 | cc.Env = os.Environ() 24 | ob := bytes.Buffer{} 25 | cc.Stdout = &ob 26 | if err := cc.Run(); err != nil { 27 | return nil, cobra.ShellCompDirectiveError 28 | } 29 | names := strings.Fields(ob.String()) 30 | if ln := len(names) - 1; ln >= 0 { 31 | // The last name is the directive in the form of a colon followed by a digit. 32 | if m := directiveCodeRx.FindStringSubmatch(names[ln]); m != nil { 33 | n, _ := strconv.Atoi(m[1]) 34 | return names[:ln], cobra.ShellCompDirective(n) 35 | } 36 | } 37 | return nil, cobra.ShellCompDirectiveError 38 | } 39 | -------------------------------------------------------------------------------- /pkg/client/cli/env/flags.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type Flags struct { 8 | File string // --env-file 9 | Syntax Syntax // --env-syntax 10 | JSON string // --env-json 11 | } 12 | 13 | func (f *Flags) AddFlags(flagSet *pflag.FlagSet) { 14 | flagSet.StringVarP(&f.File, "env-file", "e", "", ``+ 15 | `Also emit the remote environment to an file. The syntax used in the file can be determined using flag --env-syntax`) 16 | 17 | flagSet.Var(&f.Syntax, "env-syntax", `Syntax used for env-file. One of `+SyntaxUsage()) 18 | 19 | flagSet.StringVarP(&f.JSON, "env-json", "j", "", `Also emit the remote environment to a file as a JSON blob.`) 20 | } 21 | 22 | func (f *Flags) MaybeWrite(env map[string]string) error { 23 | if f.File != "" { 24 | if err := f.Syntax.writeFile(f.File, env); err != nil { 25 | return err 26 | } 27 | } 28 | if f.JSON != "" { 29 | if err := SyntaxJSON.writeFile(f.JSON, env); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/context.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | type flagSetsKey struct{} 10 | 11 | func WithFlagSets(ctx context.Context, flagSets ...*pflag.FlagSet) context.Context { 12 | if len(flagSets) > 0 { 13 | ctx = context.WithValue(ctx, flagSetsKey{}, flagSets) 14 | } 15 | return ctx 16 | } 17 | 18 | func GetFlagSets(ctx context.Context) []*pflag.FlagSet { 19 | if fs, ok := ctx.Value(flagSetsKey{}).([]*pflag.FlagSet); ok { 20 | return fs 21 | } 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/deprecation.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/client/cli/output" 7 | "github.com/telepresenceio/telepresence/v2/pkg/ioutil" 8 | ) 9 | 10 | // DeprecationIfChanged will print a deprecation warning on output.Info if the flag has changed. 11 | // 12 | // Use this method instead of the standard pflag deprecation to ensure that the deprecation message 13 | // doesn't clobber JSON output. 14 | func DeprecationIfChanged(cmd *cobra.Command, flagName, alternative string) { 15 | if flag := cmd.Flag(flagName); flag != nil && flag.Changed { 16 | ioutil.Printf(output.Info(cmd.Context()), "Flag --%s has been deprecated, %s\n", flagName, alternative) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pkg/client/cli/flags/map.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/slice" 7 | ) 8 | 9 | // Map returns a map of the flags that has been modified in the given FlagSet. 10 | func Map(flags *pflag.FlagSet) map[string]string { 11 | if flags == nil { 12 | return nil 13 | } 14 | flagMap := make(map[string]string, flags.NFlag()) 15 | flags.VisitAll(func(flag *pflag.Flag) { 16 | if flag.Changed { 17 | var v string 18 | if sv, ok := flag.Value.(pflag.SliceValue); ok { 19 | v = slice.AsCSV(sv.GetSlice()) 20 | } else { 21 | v = flag.Value.String() 22 | } 23 | flagMap[flag.Name] = v 24 | } 25 | }) 26 | return flagMap 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/cli/global/flags.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | const ( 8 | FlagContext = "context" 9 | FlagDocker = "docker" 10 | FlagNoReport = "no-report" 11 | FlagOutput = "output" 12 | FlagProgress = "progress" 13 | FlagUse = "use" 14 | ) 15 | 16 | var FlagNames = []string{FlagContext, FlagDocker, FlagNoReport, FlagOutput, FlagProgress, FlagUse} //nolint:gochecknoglobals // constant names 17 | 18 | func Flags(hasKubeFlags bool) *pflag.FlagSet { 19 | flags := pflag.NewFlagSet("", 0) 20 | if !hasKubeFlags { 21 | // Add deprecated global connect and docker flags. 22 | flags.String(FlagContext, "", "") 23 | flags.Lookup(FlagContext).Hidden = true 24 | flags.Bool(FlagDocker, false, "") 25 | flags.Lookup(FlagDocker).Hidden = true 26 | } 27 | flags.Bool(FlagNoReport, false, "") 28 | f := flags.Lookup(FlagNoReport) 29 | f.Hidden = true 30 | f.Deprecated = "not used" 31 | flags.String(FlagUse, "", "Match expression that uniquely identifies the daemon container") 32 | flags.String(FlagOutput, "default", "Set the output format, supported values are 'json', 'yaml', and 'default'") 33 | flags.String(FlagProgress, "auto", `Set type of progress output (auto, tty, plain, json, quiet)`) 34 | return flags 35 | } 36 | -------------------------------------------------------------------------------- /pkg/client/cli/mount/prepare_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package mount 5 | 6 | import ( 7 | "context" 8 | "os" 9 | "path/filepath" 10 | 11 | "github.com/telepresenceio/telepresence/v2/pkg/client" 12 | ) 13 | 14 | func prepare(ctx context.Context, cwd string, mountPoint string) (string, error) { 15 | if mountPoint == "" { 16 | return os.MkdirTemp(client.GetConfig(ctx).Intercept().MountsRoot, "telfs-") 17 | } 18 | 19 | // filepath.Abs uses os.Getwd but we need the working dir of the cli 20 | if !filepath.IsAbs(mountPoint) { 21 | mountPoint = filepath.Join(cwd, mountPoint) 22 | mountPoint = filepath.Clean(mountPoint) 23 | } 24 | 25 | return mountPoint, os.MkdirAll(mountPoint, 0o700) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/client/cli/mount/prepare_windows.go: -------------------------------------------------------------------------------- 1 | package mount 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "io/fs" 8 | "os" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/errcat" 11 | ) 12 | 13 | func prepare(_ context.Context, _ string, mountPoint string) (string, error) { 14 | var err error 15 | if mountPoint == "" { 16 | // Find a free drive letter. Background at T, loop around and skip C and D, 17 | // A and B aren't often used nowadays. No floppy-disks. 18 | for _, c := range "TUVXYZABEFGHIJKLMNOPQR" { 19 | _, err = os.Stat(fmt.Sprintf(`%c:\`, c)) 20 | if errors.Is(err, fs.ErrNotExist) { 21 | return fmt.Sprintf(`%c:`, c), nil 22 | } 23 | } 24 | return "", errcat.User.New("found no available drive to use as mount point") 25 | } 26 | 27 | // Mount point must be a drive letter 28 | ok := len(mountPoint) == 2 && mountPoint[1] == ':' 29 | if ok { 30 | dl := mountPoint[0] 31 | ok = dl >= 'A' && dl <= 'Z' || dl >= 'a' && dl <= 'z' 32 | } 33 | if !ok { 34 | err = errcat.User.New("mount point must be a drive letter followed by a colon") 35 | } 36 | return mountPoint, err 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/cli/progress/noop.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Docker Compose CLI authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package progress 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | type noopWriter struct{} 24 | 25 | func (p noopWriter) Start(context.Context, string) { 26 | } 27 | 28 | func (p noopWriter) IsNoOp() bool { 29 | return true 30 | } 31 | 32 | func (p noopWriter) Write(...*Event) { 33 | } 34 | 35 | func (p noopWriter) TailMsgf(_ string, _ ...any) { 36 | } 37 | 38 | func (p noopWriter) Stop() { 39 | } 40 | -------------------------------------------------------------------------------- /pkg/client/cli/progress/quiet.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Docker Compose CLI authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package progress 18 | 19 | import "context" 20 | 21 | type quiet struct{} 22 | 23 | func (q quiet) Start(context.Context, string) { 24 | } 25 | 26 | func (q quiet) IsNoOp() bool { 27 | return false 28 | } 29 | 30 | func (q quiet) Stop() { 31 | } 32 | 33 | func (q quiet) Write(...*Event) { 34 | } 35 | 36 | func (q quiet) TailMsgf(_ string, _ ...any) { 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/cmd_error.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/datawire/dlib/dexec" 7 | ) 8 | 9 | // RunError checks if the given err is a *exit.ExitError, and if so, extracts 10 | // Stderr and the ExitCode from it. 11 | func RunError(err error) error { 12 | if ee, ok := err.(*dexec.ExitError); ok { 13 | if len(ee.Stderr) > 0 { 14 | err = fmt.Errorf("%s, exit code %d", string(ee.Stderr), ee.ExitCode()) 15 | } else { 16 | err = fmt.Errorf("exit code %d", ee.ExitCode()) 17 | } 18 | } 19 | return err 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/config_darwin.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | const defaultDockerAddHostGateway = false 4 | -------------------------------------------------------------------------------- /pkg/client/config_linux.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | const defaultDockerAddHostGateway = true 4 | -------------------------------------------------------------------------------- /pkg/client/config_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | 3 | package client 4 | 5 | import "net/netip" 6 | 7 | // defaultVirtualSubnet A randomly chosen class E subnet. 8 | var defaultVirtualSubnet = netip.MustParsePrefix("246.246.0.0/16") //nolint:gochecknoglobals // constant 9 | 10 | type OSSpecificConfig struct{} 11 | 12 | func GetDefaultOSSpecificConfig() OSSpecificConfig { 13 | return OSSpecificConfig{} 14 | } 15 | 16 | func (c *OSSpecificConfig) Merge(o *OSSpecificConfig) { 17 | } 18 | -------------------------------------------------------------------------------- /pkg/client/config_util.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/datawire/dlib/dlog" 7 | "github.com/telepresenceio/telepresence/v2/pkg/log" 8 | ) 9 | 10 | // ReloadDaemonLogLevel calls SetLevel with the log level defined 11 | // for the rootDaemon or userDaemon 12 | // depending on the root flag. Assumes that the config has already been reloaded. 13 | func ReloadDaemonLogLevel(c context.Context, root bool) error { 14 | newCfg := GetConfig(c) 15 | var level string 16 | if root { 17 | level = newCfg.LogLevels().RootDaemon.String() 18 | } else { 19 | level = newCfg.LogLevels().UserDaemon.String() 20 | } 21 | log.SetLevel(c, level) 22 | dlog.Info(c, "Configuration reloaded") 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/client/docker/container.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/docker/docker/api/types/container" 9 | 10 | "github.com/datawire/dlib/dlog" 11 | "github.com/telepresenceio/telepresence/v2/pkg/client" 12 | ) 13 | 14 | func StopContainer(ctx context.Context, nameOrID string) error { 15 | cli, err := GetClient(ctx) 16 | if err != nil { 17 | return err 18 | } 19 | opts := container.StopOptions{} 20 | timeout := client.GetConfig(ctx).Timeouts().Get(client.TimeoutContainerShutdown) 21 | if timeout > 0 { 22 | secs := int(timeout / time.Second) 23 | opts.Timeout = &secs 24 | dlog.Debugf(ctx, "Stopping container %s with a grace period of %d seconds", nameOrID, secs) 25 | } else { 26 | dlog.Debugf(ctx, "Stopping container %s with default grace period", nameOrID) 27 | } 28 | _, err = cli.ContainerInspect(ctx, nameOrID) 29 | if err != nil { 30 | dlog.Errorf(ctx, "Failed to inspect container %s: %v", nameOrID, err) 31 | return err 32 | } 33 | err = cli.ContainerStop(ctx, nameOrID, opts) 34 | if err != nil { 35 | dlog.Errorf(ctx, "Failed to stop container %s: %v", nameOrID, err) 36 | return fmt.Errorf("failed to stop container %s: %v", nameOrID, err) 37 | } 38 | dlog.Debugf(ctx, "Container %s stopped", nameOrID) 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/docker/daemon_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/ioutil" 7 | ) 8 | 9 | func TestSafeContainerName(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | want string 13 | }{ 14 | { 15 | "@", 16 | "a", 17 | }, 18 | { 19 | "@x", 20 | "ax", 21 | }, 22 | { 23 | "x@", 24 | "x_", 25 | }, 26 | { 27 | "x@y", 28 | "x_y", 29 | }, 30 | { 31 | "x™y", // multibyte char 32 | "x_y", 33 | }, 34 | { 35 | "x™", // multibyte char 36 | "x_", 37 | }, 38 | { 39 | "_y", 40 | "ay", 41 | }, 42 | { 43 | "_y_", 44 | "ay_", 45 | }, 46 | // TODO: Add test cases. 47 | } 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | if got := ioutil.SafeName(tt.name); got != tt.want { 51 | t.Errorf("SafeName() = %v, want %v", got, tt.want) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pkg/client/docker/volume_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/blang/semver/v4" 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | "github.com/telepresenceio/telepresence/v2/pkg/client" 11 | ) 12 | 13 | func Test_getLatestPluginVersion(t *testing.T) { 14 | c := dlog.NewTestContext(t, false) 15 | env, err := client.LoadEnv() 16 | require.NoError(t, err) 17 | c = client.WithEnv(c, env) 18 | 19 | cfg, err := client.LoadConfig(c) 20 | require.NoError(t, err) 21 | c = client.WithConfig(c, cfg) 22 | 23 | ver, err := getLatestPluginVersion(c, pluginName(c)) 24 | require.NoError(t, err) 25 | require.True(t, ver.EQ(zeroVersion) || semver.MustParse("0.1.3").LT(ver)) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/client/ensured_state.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | type ( 10 | Prolog func(context.Context) (acquired bool, err error) 11 | Action func(context.Context) error 12 | ) 13 | 14 | // WithEnsuredState calls prolog, and if that was successful, calls act. If epilog is not nil, it is guaranteed 15 | // to be called when prolog returns true. 16 | func WithEnsuredState(ctx context.Context, prolog Prolog, action, epilog Action) error { 17 | wasAcquired, err := prolog(ctx) 18 | if err != nil { 19 | return err 20 | } 21 | if wasAcquired && epilog != nil { 22 | defer func() { 23 | // The context might have been cancelled, so we use the original context 24 | // without cancellation, but with a deactivation timeout of 10 seconds. 25 | ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 10*time.Second) 26 | defer cancel() 27 | if cerr := epilog(ctx); cerr != nil { 28 | if err == nil { 29 | err = cerr 30 | } else { 31 | err = fmt.Errorf("%w\n%v", err, cerr) 32 | } 33 | } 34 | }() 35 | } 36 | if action != nil { 37 | err = action(ctx) 38 | } 39 | return err 40 | } 41 | -------------------------------------------------------------------------------- /pkg/client/envconfig_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package client 5 | 6 | type OSSpecificEnv struct { 7 | Shell string `env:"SHELL, parser=nonempty-string,default=/bin/bash"` 8 | } 9 | -------------------------------------------------------------------------------- /pkg/client/envconfig_windows.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | type OSSpecificEnv struct { 4 | Shell string `env:"ComSpec, parser=nonempty-string,default=C:\\WINDOWS\\system32\\cmd.exe"` 5 | } 6 | -------------------------------------------------------------------------------- /pkg/client/free_ports.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "net" 4 | 5 | // FreePortsTCP uses net.Listen repeatedly to choose free TCP ports for the localhost. It then immediately closes 6 | // the listeners and returns the addresses that were allocated. 7 | // 8 | // NOTE: Since the listeners are closed, there's a chance that someone else might allocate the returned addresses 9 | // before they are actually used. The chances are slim though, since tests show that in most cases (at least on 10 | // macOS and Linux), the same address isn't allocated for a while even if the allocation is made from different 11 | // processes. 12 | func FreePortsTCP(count int) ([]*net.TCPAddr, error) { 13 | ls := make([]net.Listener, 0, count) 14 | as := make([]*net.TCPAddr, count) 15 | defer func() { 16 | for _, l := range ls { 17 | _ = l.Close() 18 | } 19 | }() 20 | 21 | for i := 0; i < count; i++ { 22 | if l, err := net.Listen("tcp", "localhost:0"); err != nil { 23 | return nil, err 24 | } else { 25 | ls = append(ls, l) 26 | as[i] = l.Addr().(*net.TCPAddr) 27 | } 28 | } 29 | return as, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/client/install_id.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io/fs" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/google/uuid" 11 | 12 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 13 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 14 | ) 15 | 16 | func InstallID(ctx context.Context) (string, error) { 17 | idFile := filepath.Join(filelocation.AppUserConfigDir(ctx), "id") 18 | data, err := dos.ReadFile(ctx, idFile) 19 | switch { 20 | case err == nil: 21 | return strings.TrimSpace(string(data)), nil 22 | case errors.Is(err, fs.ErrNotExist): 23 | id := uuid.New().String() 24 | if err = dos.WriteFile(ctx, idFile, []byte(id), 0o644); err != nil { 25 | return "", err 26 | } 27 | return id, nil 28 | default: 29 | return "", err 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/client/k8sclient/cani.go: -------------------------------------------------------------------------------- 1 | package k8sclient 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/authorization/v1" 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/k8sapi" 9 | ) 10 | 11 | // CanPortForward answers the question if this client has the RBAC permissions necessary 12 | // to perform a port-forward to the connected namespace. 13 | func CanPortForward(ctx context.Context, namespace string) bool { 14 | ok, err := k8sapi.CanI(ctx, &v1.ResourceAttributes{ 15 | Verb: "create", 16 | Resource: "pods", 17 | Subresource: "portforward", 18 | Namespace: namespace, 19 | }) 20 | return err == nil && ok 21 | } 22 | -------------------------------------------------------------------------------- /pkg/client/k8sclient/config.go: -------------------------------------------------------------------------------- 1 | package k8sclient 2 | 3 | import ( 4 | "context" 5 | 6 | "k8s.io/client-go/rest" 7 | ) 8 | 9 | type k8sConfigKey struct{} 10 | 11 | func GetK8sConfig(ctx context.Context) *rest.Config { 12 | if c, ok := ctx.Value(k8sConfigKey{}).(*rest.Config); ok { 13 | return c 14 | } 15 | return nil 16 | } 17 | 18 | func WithK8sConfig(ctx context.Context, k8sConfig *rest.Config) context.Context { 19 | return context.WithValue(ctx, k8sConfigKey{}, k8sConfig) 20 | } 21 | -------------------------------------------------------------------------------- /pkg/client/logging/dup_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package logging 5 | 6 | import ( 7 | "os" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | // dupToStdOut ensures that anything written to stdout will end up in the given file. 13 | func dupToStdOut(file *os.File) error { 14 | // https://github.com/golang/go/issues/325 15 | if err := unix.Dup2(int(file.Fd()), 1); err != nil { 16 | return err 17 | } 18 | os.Stdout = file 19 | return nil 20 | } 21 | 22 | // dupToStdErr ensures that anything written to stderr will end up in the given file. 23 | func dupToStdErr(file *os.File) error { 24 | // https://github.com/golang/go/issues/325 25 | if err := unix.Dup2(int(file.Fd()), 2); err != nil { 26 | return err 27 | } 28 | os.Stderr = file 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/client/logging/dup_windows.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "os" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | func dupToStdOut(file *os.File) error { 10 | if err := windows.SetStdHandle(windows.STD_OUTPUT_HANDLE, windows.Handle(file.Fd())); err != nil { 11 | return err 12 | } 13 | os.Stdout = file 14 | return nil 15 | } 16 | 17 | func dupToStdErr(file *os.File) error { 18 | // https://stackoverflow.com/questions/34772012/capturing-panic-in-golang/34772516 19 | if err := windows.SetStdHandle(windows.STD_ERROR_HANDLE, windows.Handle(file.Fd())); err != nil { 20 | return err 21 | } 22 | os.Stderr = file 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/client/logging/initcontext_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package logging 5 | 6 | import ( 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | func dupStd() (func(), error) { 11 | stdoutFd, err := unix.Dup(1) 12 | if err != nil { 13 | return nil, err 14 | } 15 | stderrFd, err := unix.Dup(2) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return func() { 20 | _ = unix.Dup2(stdoutFd, 1) 21 | _ = unix.Dup2(stderrFd, 2) 22 | }, nil 23 | } 24 | -------------------------------------------------------------------------------- /pkg/client/logging/initcontext_windows_test.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | func dupStd() (func(), error) { 4 | return func() {}, nil 5 | } 6 | -------------------------------------------------------------------------------- /pkg/client/logging/rotatingfile_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package logging 5 | 6 | import ( 7 | "time" 8 | 9 | "golang.org/x/term" 10 | ) 11 | 12 | // restoreCTimeAfterRename is a noop on unixes since the renamed file retains the creation time of the source. 13 | func restoreCTimeAfterRename(_ string, _ time.Time) error { 14 | return nil 15 | } 16 | 17 | // IsTerminal returns whether the given file descriptor is a terminal. 18 | var IsTerminal = term.IsTerminal //nolint:gochecknoglobals // os specific func replacement 19 | -------------------------------------------------------------------------------- /pkg/client/logging/rotatingfile_windows.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "time" 5 | 6 | "golang.org/x/sys/windows" 7 | ) 8 | 9 | // IsTerminal returns whether the given file descriptor is a terminal. 10 | var IsTerminal = func(fd int) bool { //nolint:gochecknoglobals // os specific func replacement 11 | return false 12 | } 13 | 14 | // restoreCTimeAfterRename will restore the creation time on a file on Windows where 15 | // the file otherwise gets the creation time of the existing file that the operation 16 | // overwrites. 17 | func restoreCTimeAfterRename(path string, ctime time.Time) error { 18 | p16, e := windows.UTF16PtrFromString(path) 19 | if e != nil { 20 | return e 21 | } 22 | h, e := windows.CreateFile(p16, 23 | windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, 24 | windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) 25 | if e != nil { 26 | return e 27 | } 28 | defer windows.Close(h) 29 | c := windows.NsecToFiletime(ctime.UnixNano()) 30 | return windows.SetFileTime(h, &c, nil, nil) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/client/logging/stat.go: -------------------------------------------------------------------------------- 1 | package logging 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/telepresenceio/telepresence/v2/pkg/dos" 8 | ) 9 | 10 | // FStat returns the file status/info of an open file. 11 | func FStat(file dos.File) (SysInfo, error) { 12 | return osFStat(file) 13 | } 14 | 15 | // SysInfo represents the elaborate info in a FileInfo.Sys(). The implementations are 16 | // os specific. 17 | // 18 | // Unix: 19 | // 20 | // info.Sys().(*syscall.Stat_t) 21 | // 22 | // Windows: 23 | // 24 | // info.Sys().(*syscall.Win32FileAttributeData) 25 | type SysInfo interface { 26 | fmt.Stringer 27 | 28 | Size() int64 29 | 30 | BirthTime() time.Time 31 | ModifyTime() time.Time // most recent content change 32 | ChangeTime() time.Time // most recent metadata change 33 | 34 | SetOwnerAndGroup(name string) error 35 | 36 | HaveSameOwnerAndGroup(SysInfo) bool 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/logging/stat_linux_test.go: -------------------------------------------------------------------------------- 1 | package logging_test 2 | 3 | import ( 4 | "errors" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | var stat unix.Statx_t 11 | err := unix.Statx(-1, "/", 0, unix.STATX_BTIME, &stat) 12 | if err != nil && errors.Is(err, unix.ENOSYS) { 13 | osHasBTime = false 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_docker.go: -------------------------------------------------------------------------------- 1 | //go:build docker 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | "sync" 9 | 10 | "github.com/telepresenceio/go-fuseftp/rpc" 11 | ) 12 | 13 | // NewFTPMounter returns nil. It's here to satisfy the linker. 14 | func NewFTPMounter(rpc.FuseFTPClient, *sync.WaitGroup) Mounter { 15 | return nil 16 | } 17 | 18 | type fuseFtpMgr struct{} 19 | 20 | type FuseFTPManager interface { 21 | DeferInit(context.Context) error 22 | GetFuseFTPClient(context.Context) rpc.FuseFTPClient 23 | } 24 | 25 | func NewFuseFTPManager() FuseFTPManager { 26 | return &fuseFtpMgr{} 27 | } 28 | 29 | func (s *fuseFtpMgr) DeferInit(context.Context) error { 30 | return errors.New("fuseftp client is not available") 31 | } 32 | 33 | func (s *fuseFtpMgr) GetFuseFTPClient(context.Context) rpc.FuseFTPClient { 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_embedded.go: -------------------------------------------------------------------------------- 1 | //go:build !(docker || external_fuseftp || linked_fuseftp) 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | _ "embed" 8 | "errors" 9 | "io/fs" 10 | "os" 11 | "path/filepath" 12 | 13 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 14 | ) 15 | 16 | //go:embed fuseftp.bits 17 | var fuseftpBits []byte 18 | 19 | func getFuseFTPServer(ctx context.Context, exe string) (string, error) { 20 | qn := filepath.Join(filelocation.AppUserCacheDir(ctx), exe) 21 | var sz int 22 | st, err := os.Stat(qn) 23 | if err != nil { 24 | if !errors.Is(err, fs.ErrNotExist) { 25 | return "", err 26 | } 27 | sz = 0 28 | } else { 29 | sz = int(st.Size()) 30 | } 31 | if len(fuseftpBits) != sz { 32 | err = os.WriteFile(qn, fuseftpBits, 0o700) 33 | } 34 | return qn, err 35 | } 36 | -------------------------------------------------------------------------------- /pkg/client/remotefs/fuseftp_external.go: -------------------------------------------------------------------------------- 1 | //go:build external_fuseftp && !docker 2 | 3 | package remotefs 4 | 5 | import ( 6 | "context" 7 | "os/exec" //nolint:depguard // No use for dexec here 8 | ) 9 | 10 | func getFuseFTPServer(_ context.Context, exe string) (string, error) { 11 | return exec.LookPath(exe) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/client/remotefs/mounter.go: -------------------------------------------------------------------------------- 1 | package remotefs 2 | 3 | import ( 4 | "context" 5 | "net/netip" 6 | ) 7 | 8 | // A Mounter is responsible for mounting a remote filesystem in a local directory or drive letter. 9 | type Mounter interface { 10 | // Start mounts the remote directory given by mountPoint on the local directory or drive letter 11 | // given ty clientMountPoint. The podIP and port is the address to the remote FTP or SFTP server. 12 | // The id is just used for logging purposes. 13 | Start(ctx context.Context, workload, container, clientMountPoint, mountPoint string, podAddrPort netip.AddrPort, ro bool) error 14 | } 15 | -------------------------------------------------------------------------------- /pkg/client/rootd/dns/client_queue.go: -------------------------------------------------------------------------------- 1 | package dns 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | // Most of this was aped off the docs at https://pkg.go.dev/container/heap@go1.17.8 10 | 11 | type waitingClient struct { 12 | returnCh chan *dns.Conn 13 | doneCh <-chan struct{} 14 | arrivalTime time.Time 15 | index int // The index of the item in the heap. 16 | } 17 | 18 | // A clientQueue implements heap.Interface and holds waitingClients. 19 | type clientQueue []*waitingClient 20 | 21 | func (pq clientQueue) Len() int { return len(pq) } 22 | 23 | func (pq clientQueue) Less(i, j int) bool { 24 | return pq[i].arrivalTime.Before(pq[j].arrivalTime) 25 | } 26 | 27 | func (pq clientQueue) Swap(i, j int) { 28 | pq[i], pq[j] = pq[j], pq[i] 29 | pq[i].index = i 30 | pq[j].index = j 31 | } 32 | 33 | func (pq *clientQueue) Push(x any) { 34 | n := len(*pq) 35 | item := x.(*waitingClient) 36 | item.index = n 37 | *pq = append(*pq, item) 38 | } 39 | 40 | func (pq *clientQueue) Pop() any { 41 | old := *pq 42 | n := len(old) 43 | item := old[n-1] 44 | old[n-1] = nil // avoid memory leak 45 | item.index = -1 // for safety 46 | *pq = old[0 : n-1] 47 | return item 48 | } 49 | -------------------------------------------------------------------------------- /pkg/client/stream_error.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "google.golang.org/grpc/codes" 8 | "google.golang.org/grpc/status" 9 | ) 10 | 11 | // RecvEOFError should be returned when a component has returned EOF from a stream. 12 | // Do not use this if, for example, the initial dial to a stream fails. 13 | type RecvEOFError struct { 14 | msg string 15 | err error 16 | } 17 | 18 | func (e *RecvEOFError) Error() string { 19 | return fmt.Sprintf("%s: %v", e.msg, e.err) 20 | } 21 | 22 | func (e *RecvEOFError) Unwrap() error { 23 | return e.err 24 | } 25 | 26 | // WrapRecvErr wraps an error from a Recv call. If the error is nil, nil is returned. 27 | // If the error indicates that the remote end has , a RecvEOFError wrapping the error will be returned. 28 | // Otherwise, the original error will be wrapped as fmt.Errorf("%s: %w", msg, err). 29 | func WrapRecvErr(err error, msg string) error { 30 | if err == nil { 31 | return nil 32 | } 33 | if status.Code(err) == codes.Unavailable || err == io.EOF { 34 | return &RecvEOFError{msg, err} 35 | } 36 | return fmt.Errorf("%s: %w", msg, err) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/client/userd/context.go: -------------------------------------------------------------------------------- 1 | package userd 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type sessionKey struct{} 8 | 9 | func WithSession(ctx context.Context, session Session) context.Context { 10 | return context.WithValue(ctx, sessionKey{}, session) 11 | } 12 | 13 | func GetSession(ctx context.Context) Session { 14 | if s, ok := ctx.Value(sessionKey{}).(Session); ok { 15 | return s 16 | } 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /pkg/client/userd/trafficmgr/config.go: -------------------------------------------------------------------------------- 1 | package trafficmgr 2 | 3 | import ( 4 | "context" 5 | "path/filepath" 6 | 7 | empty "google.golang.org/protobuf/types/known/emptypb" 8 | 9 | "github.com/telepresenceio/telepresence/v2/pkg/client" 10 | "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 11 | ) 12 | 13 | func (s *session) GetConfig(ctx context.Context) (*client.SessionConfig, error) { 14 | nc, err := s.rootDaemon.GetNetworkConfig(ctx, &empty.Empty{}) 15 | if err != nil { 16 | return nil, err 17 | } 18 | rc := client.GetDefaultConfig() 19 | err = client.UnmarshalJSON(nc.ClientConfig, rc, true) 20 | if err != nil { 21 | return nil, err 22 | } 23 | return &client.SessionConfig{ 24 | ClientFile: filepath.Join(filelocation.AppUserConfigDir(ctx), client.ConfigFile), 25 | Config: client.GetConfig(ctx).Merge(rc), 26 | }, nil 27 | } 28 | -------------------------------------------------------------------------------- /pkg/client/userd/trafficmgr/dial_request.go: -------------------------------------------------------------------------------- 1 | package trafficmgr 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/telepresenceio/telepresence/v2/pkg/tunnel" 7 | ) 8 | 9 | func (s *session) dialRequestWatcher(ctx context.Context) error { 10 | return runWithRetry(ctx, s._dialRequestWatcher) 11 | } 12 | 13 | func (s *session) _dialRequestWatcher(ctx context.Context) error { 14 | // Deal with dial requests from the manager 15 | dialerStream, err := s.managerClient.WatchDial(ctx, s.sessionInfo) 16 | if err != nil { 17 | return err 18 | } 19 | return tunnel.DialWaitLoop( 20 | ctx, tunnel.ManagerToClient, tunnel.ManagerProvider(s.managerClient), dialerStream, tunnel.SessionID(s.sessionInfo.SessionId)) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/dnsproxy/rpc.go: -------------------------------------------------------------------------------- 1 | package dnsproxy 2 | 3 | import ( 4 | "github.com/miekg/dns" 5 | "google.golang.org/grpc/codes" 6 | "google.golang.org/grpc/status" 7 | 8 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 9 | ) 10 | 11 | func ToRPC(rrs RRs, rCode int) (*manager.DNSResponse, error) { 12 | l := 0 13 | for _, rr := range rrs { 14 | l += dns.Len(rr) 15 | } 16 | rsp := &manager.DNSResponse{RCode: int32(rCode)} 17 | rrb := make([]byte, l) 18 | off := 0 19 | for _, rr := range rrs { 20 | var err error 21 | if off, err = dns.PackRR(rr, rrb, off, nil, false); err != nil { 22 | return nil, status.Errorf(codes.Internal, "unable to pack DNS reply: %v", err) 23 | } 24 | } 25 | rsp.Rrs = rrb 26 | return rsp, nil 27 | } 28 | 29 | func FromRPC(r *manager.DNSResponse) (RRs, int, error) { 30 | rrb := r.Rrs 31 | var rrs RRs 32 | off := 0 33 | for len(rrb) > off { 34 | var rr dns.RR 35 | var err error 36 | if rr, off, err = dns.UnpackRR(rrb, off); err != nil { 37 | return nil, dns.RcodeFormatError, status.Errorf(codes.InvalidArgument, "unable to unpack DNS response: %v", err) 38 | } 39 | rrs = append(rrs, rr) 40 | } 41 | return rrs, int(r.RCode), nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/dos/exe.go: -------------------------------------------------------------------------------- 1 | package dos 2 | 3 | import ( 4 | "context" 5 | "os" 6 | ) 7 | 8 | // Exe is an abstraction of the executable related functions with the same name in the os package. 9 | type Exe interface { 10 | Executable() (string, error) 11 | } 12 | 13 | type exeKey struct{} 14 | 15 | func WithExe(ctx context.Context, exe Exe) context.Context { 16 | return context.WithValue(ctx, exeKey{}, exe) 17 | } 18 | 19 | // ExeAPI returns the Exe that has been registered with the given context, or 20 | // the instance that delegates to the corresponding functions in the os package. 21 | func ExeAPI(ctx context.Context) Exe { 22 | if e, ok := ctx.Value(exeKey{}).(Exe); ok { 23 | return e 24 | } 25 | return osExe{} 26 | } 27 | 28 | func Executable(ctx context.Context) (string, error) { 29 | return ExeAPI(ctx).Executable() 30 | } 31 | 32 | type osExe struct{} 33 | 34 | func (osExe) Executable() (string, error) { 35 | return os.Executable() 36 | } 37 | -------------------------------------------------------------------------------- /pkg/dos/package.go: -------------------------------------------------------------------------------- 1 | // Package dos contains an abstraction of some functions in the go os package. When used in code, it allows those 2 | // functions to be mocked in unit tests. 3 | // In general, the functions are implemented using an interface which is then stored in the context. The functions 4 | // are then called using dos instead of os, and with an additional first context argument. E.g. 5 | // 6 | // ctx := dos.WithFS(ctx, mockFS) 7 | // f, err := dos.Open(ctx, "/etc/resolv.conf") 8 | package dos 9 | -------------------------------------------------------------------------------- /pkg/dpipe/dpipe.go: -------------------------------------------------------------------------------- 1 | package dpipe 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "os/exec" //nolint:depguard // We want no logging and no soft-context signal handling 8 | 9 | "github.com/datawire/dlib/dlog" 10 | "github.com/telepresenceio/telepresence/v2/pkg/shellquote" 11 | ) 12 | 13 | func DPipe(ctx context.Context, peer io.ReadWriteCloser, cmdName string, cmdArgs ...string) error { 14 | defer func() { 15 | _ = peer.Close() 16 | }() 17 | 18 | cmd := exec.CommandContext(ctx, cmdName, cmdArgs...) 19 | cmd.Stdin = peer 20 | cmd.Stdout = peer 21 | cmd.Stderr = dlog.StdLogger(ctx, dlog.LogLevelError).Writer() 22 | 23 | cmdLine := shellquote.ShellString(cmd.Path, cmd.Args) 24 | if err := cmd.Start(); err != nil { 25 | return fmt.Errorf("failed to start %s: %w", cmdLine, err) 26 | } 27 | 28 | ctx = dlog.WithField(ctx, "exec.pid", cmd.Process.Pid) 29 | dlog.Infof(ctx, "started command %s", cmdLine) 30 | defer dlog.Infof(ctx, "ended command %s", cmdName) 31 | runFinished := make(chan error) 32 | go func() { 33 | defer close(runFinished) 34 | if err := cmd.Wait(); err != nil { 35 | if !cmd.ProcessState.Success() && ctx.Err() == nil { 36 | runFinished <- err 37 | } 38 | } 39 | }() 40 | 41 | select { 42 | case <-ctx.Done(): 43 | killProcess(ctx, cmd) 44 | return nil 45 | case err := <-runFinished: 46 | return err 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/dpipe/dpipe_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package dpipe 5 | 6 | import ( 7 | "context" 8 | "os" 9 | "os/exec" //nolint:depguard // We want no logging and no soft-context signal handling 10 | "time" 11 | 12 | "golang.org/x/sys/unix" 13 | ) 14 | 15 | func killProcess(_ context.Context, cmd *exec.Cmd) { 16 | // A process is sometimes not terminated gracefully by the SIGTERM, so we give 17 | // it a second to succeed and then kill it forcefully. 18 | time.AfterFunc(time.Second, func() { 19 | if cmd.ProcessState == nil { 20 | _ = cmd.Process.Signal(unix.SIGKILL) 21 | } 22 | }) 23 | _ = cmd.Process.Signal(os.Interrupt) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/dpipe/dpipe_windows.go: -------------------------------------------------------------------------------- 1 | package dpipe 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/exec" //nolint:depguard // We want no logging and no soft-context signal handling 7 | 8 | "github.com/telepresenceio/telepresence/v2/pkg/proc" 9 | ) 10 | 11 | func killProcess(ctx context.Context, cmd *exec.Cmd) { 12 | proc.KillProcessGroup(ctx, cmd, os.Kill) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/dpipe/testdata/echo/echo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | var dest string 11 | flag.StringVar(&dest, "d", "1", "Destination of output. Legal values are 1 (stdout), 2 (stderr) or a file name") 12 | flag.Parse() 13 | 14 | var out *os.File 15 | switch dest { 16 | case "1": 17 | out = os.Stdout 18 | case "2": 19 | out = os.Stderr 20 | default: 21 | var err error 22 | if out, err = os.Create(dest); err != nil { 23 | fmt.Fprintln(os.Stderr, err.Error()) 24 | os.Exit(1) 25 | } 26 | defer out.Close() 27 | } 28 | for _, s := range flag.Args() { 29 | fmt.Fprintln(out, s) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/filelocation/.editorconfig: -------------------------------------------------------------------------------- 1 | [{borrowed_os_file.go,app.go}] 2 | ; mimic Go stdlib os/file.go 3 | max_line_length = 80 4 | -------------------------------------------------------------------------------- /pkg/filelocation/misc.go: -------------------------------------------------------------------------------- 1 | package filelocation 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | // SystemConfigDirs returns a list of directories to look for configuration files in. The list is 9 | // ordered in highest-precedence to lowest-precedence. Every one of these directories is however 10 | // lower precedence than UserConfigdir. 11 | // 12 | // This returns the value of the $XDG_CONFIG_DIRS environment variable if non-empty, or else 13 | // "/etc/xdg". 14 | // 15 | // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 16 | // 17 | // FIXME(lukeshu): Consider having SystemConfigDirs do something different on darwin/windows/plan9. 18 | // 19 | // FIXME(lukeshu): Consider having SystemConfigDirs vary depending on the installation prefix. 20 | func systemConfigDirs() []string { 21 | str := os.Getenv("XDG_CONFIG_DIRS") 22 | if str == "" { 23 | str = "/etc/xdg" 24 | } 25 | return filepath.SplitList(str) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/informer/factory.go: -------------------------------------------------------------------------------- 1 | package informer 2 | 3 | import ( 4 | "k8s.io/client-go/informers" 5 | 6 | argorolloutsinformer "github.com/datawire/argo-rollouts-go-client/pkg/client/informers/externalversions" 7 | ) 8 | 9 | type GlobalFactory interface { 10 | GetK8sInformerFactory() informers.SharedInformerFactory 11 | GetArgoRolloutsInformerFactory() argorolloutsinformer.SharedInformerFactory 12 | } 13 | 14 | func NewDefaultGlobalFactory(k8s informers.SharedInformerFactory, argoRollouts argorolloutsinformer.SharedInformerFactory) GlobalFactory { 15 | return &defaultGlobalFactory{k8s: k8s, argoRollouts: argoRollouts} 16 | } 17 | 18 | type defaultGlobalFactory struct { 19 | k8s informers.SharedInformerFactory 20 | argoRollouts argorolloutsinformer.SharedInformerFactory 21 | } 22 | 23 | func (f *defaultGlobalFactory) GetK8sInformerFactory() informers.SharedInformerFactory { 24 | return f.k8s 25 | } 26 | 27 | func (f *defaultGlobalFactory) GetArgoRolloutsInformerFactory() argorolloutsinformer.SharedInformerFactory { 28 | return f.argoRollouts 29 | } 30 | -------------------------------------------------------------------------------- /pkg/ioutil/print.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | // Print is like Fprint but panics on error. 10 | func Print(out io.Writer, args ...any) int { 11 | n, err := fmt.Fprint(out, args...) 12 | if err != nil { 13 | panic(err) 14 | } 15 | return n 16 | } 17 | 18 | // Println is like Fprintln but panics on error. 19 | func Println(out io.Writer, args ...any) int { 20 | n, err := fmt.Fprintln(out, args...) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return n 25 | } 26 | 27 | // Printf is like Fprintf but panics on error. 28 | func Printf(out io.Writer, format string, args ...any) int { 29 | n, err := fmt.Fprintf(out, format, args...) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return n 34 | } 35 | 36 | // WriteString is like io.WriteString but panics on error. 37 | func WriteString(out io.Writer, s string) int { 38 | n, err := io.WriteString(out, s) 39 | if err != nil { 40 | panic(err) 41 | } 42 | return n 43 | } 44 | 45 | func WriterToString(wt func(w io.Writer) (int64, error)) string { 46 | var sb strings.Builder 47 | _, err := wt(&sb) 48 | if err != nil { 49 | panic(err) 50 | } 51 | return sb.String() 52 | } 53 | -------------------------------------------------------------------------------- /pkg/ioutil/safename.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "strings" 4 | 5 | // SafeName returns a string that can safely be used as a file name or docker container. Only 6 | // characters [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed. Others are replaced by an underscore, or 7 | // if it's the very first character, by the character 'a'. 8 | func SafeName(name string) string { 9 | n := strings.Builder{} 10 | for i, c := range name { 11 | switch { 12 | case (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'): 13 | n.WriteByte(byte(c)) 14 | case i > 0 && (c == '_' || c == '.' || c == '-'): 15 | n.WriteByte(byte(c)) 16 | case i > 0: 17 | n.WriteByte('_') 18 | default: 19 | n.WriteByte('a') 20 | } 21 | } 22 | return n.String() 23 | } 24 | -------------------------------------------------------------------------------- /pkg/ioutil/tempname.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "os" 4 | 5 | // CreateTempName creates a new temporary file using os.CreateTemp, removes it, and then returns its name. 6 | func CreateTempName(dir, pattern string) (string, error) { 7 | f, err := os.CreateTemp(dir, pattern) 8 | if err != nil { 9 | return "", err 10 | } 11 | path := f.Name() 12 | _ = f.Close() 13 | _ = os.Remove(path) 14 | return path, nil 15 | } 16 | -------------------------------------------------------------------------------- /pkg/ioutil/writeallto.go: -------------------------------------------------------------------------------- 1 | package ioutil 2 | 3 | import "io" 4 | 5 | // WriterTos implements something that can be represented as a list of io.WriterTo. 6 | type WriterTos interface { 7 | WriterTos() []io.WriterTo 8 | } 9 | 10 | // WriteAllTo calls WriteTo on all elements of the WriterTo slice and 11 | // returns the total number of bytes written. 12 | func WriteAllTo(out io.Writer, wts ...io.WriterTo) (tn int64, err error) { 13 | for _, wt := range wts { 14 | if wt == nil { 15 | continue 16 | } 17 | var n int64 18 | if n, err = wt.WriteTo(out); err != nil { 19 | return tn, err 20 | } 21 | tn += n 22 | } 23 | return tn, nil 24 | } 25 | -------------------------------------------------------------------------------- /pkg/ipproto/ipproto.go: -------------------------------------------------------------------------------- 1 | // Package ipproto contains IP protocol numbers 2 | // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 3 | package ipproto 4 | 5 | import "fmt" 6 | 7 | const ( 8 | TCP = 6 9 | UDP = 17 10 | ICMP = 1 11 | ICMPV6 = 58 12 | ) 13 | 14 | // Parse returns the IP protocol for the given network. Currently only supports 15 | // TCP, UDP, and ICMP. 16 | func Parse(network string) int { 17 | switch network { 18 | case "tcp", "tcp4": 19 | return TCP 20 | case "udp", "udp4", "udp6": 21 | return UDP 22 | case "icmp": 23 | return ICMP 24 | case "icmpv6": 25 | return ICMPV6 26 | default: 27 | return -1 28 | } 29 | } 30 | 31 | func String(proto int) string { 32 | switch proto { 33 | case ICMP: 34 | return "icmp" 35 | case TCP: 36 | return "tcp" 37 | case UDP: 38 | return "udp" 39 | case ICMPV6: 40 | return "icmpv6" 41 | default: 42 | return fmt.Sprintf("IP-protocol %d", proto) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/iputil/ipnet.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "net/netip" 5 | 6 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 7 | ) 8 | 9 | func PrefixToRPC(n netip.Prefix) *manager.IPNet { 10 | return &manager.IPNet{ 11 | Ip: n.Addr().AsSlice(), 12 | Mask: int32(n.Bits()), 13 | } 14 | } 15 | 16 | func PrefixesToRPC(n []netip.Prefix) []*manager.IPNet { 17 | l := len(n) 18 | if l == 0 { 19 | return nil 20 | } 21 | ss := make([]*manager.IPNet, l) 22 | for i, m := range n { 23 | ss[i] = PrefixToRPC(m) 24 | } 25 | return ss 26 | } 27 | 28 | func RPCToPrefix(m *manager.IPNet) netip.Prefix { 29 | if a, ok := netip.AddrFromSlice(m.Ip); ok { 30 | return netip.PrefixFrom(a, int(m.Mask)) 31 | } 32 | return netip.Prefix{} 33 | } 34 | 35 | func RPCsToPrefixes(n []*manager.IPNet) []netip.Prefix { 36 | l := len(n) 37 | if l == 0 { 38 | return nil 39 | } 40 | ss := make([]netip.Prefix, l) 41 | for i, m := range n { 42 | ss[i] = RPCToPrefix(m) 43 | } 44 | return ss 45 | } 46 | -------------------------------------------------------------------------------- /pkg/iputil/join.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | ) 7 | 8 | func JoinIpPort(ip net.IP, port uint16) string { 9 | ps := strconv.Itoa(int(port)) 10 | if ip4 := ip.To4(); ip4 != nil { 11 | return ip4.String() + ":" + ps 12 | } 13 | if ip16 := ip.To16(); ip16 != nil { 14 | return "[" + ip16.String() + "]:" + ps 15 | } 16 | return ":" + ps 17 | } 18 | 19 | func JoinHostPort(host string, port uint16) string { 20 | return net.JoinHostPort(host, strconv.Itoa(int(port))) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/iputil/normalize.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "net" 5 | "strings" 6 | ) 7 | 8 | func IsIpV6Addr(ipAddStr string) bool { 9 | return strings.Count(ipAddStr, ":") >= 2 10 | } 11 | 12 | // Normalize returns the four byte version of an IPv4, even if it 13 | // was expressed as a 16 byte IP. 14 | func Normalize(ip net.IP) net.IP { 15 | if ip4 := ip.To4(); ip4 != nil { 16 | ip = ip4 17 | } 18 | return ip 19 | } 20 | -------------------------------------------------------------------------------- /pkg/iputil/parse.go: -------------------------------------------------------------------------------- 1 | package iputil 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/netip" 7 | ) 8 | 9 | // Parse is like net.ParseIP but converts an IPv4 in 16 byte form to its 4 byte form. 10 | func Parse(ipStr string) (ip netip.Addr) { 11 | if ip, err := netip.ParseAddr(ipStr); err == nil { 12 | return ip.Unmap() 13 | } 14 | return 15 | } 16 | 17 | // SplitToIPPort splits the given address into an IP and a port number. It's 18 | // an error if the address is based on a hostname rather than an IP. 19 | func SplitToIPPort(netAddr net.Addr) (netip.AddrPort, error) { 20 | ipAddr, ok := netAddr.(interface{ AddrPort() netip.AddrPort }) 21 | if !ok { 22 | return netip.AddrPort{}, fmt.Errorf("address %q is not an IP:port address", netAddr) 23 | } 24 | return ipAddr.AddrPort(), nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/k8sapi/clientconfigprovider.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import "k8s.io/client-go/tools/clientcmd" 4 | 5 | type ClientConfigProvider interface { 6 | ClientConfig() (clientcmd.ClientConfig, error) 7 | } 8 | -------------------------------------------------------------------------------- /pkg/k8sapi/interface.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import ( 4 | "k8s.io/client-go/kubernetes" 5 | 6 | argoRollouts "github.com/datawire/argo-rollouts-go-client/pkg/client/clientset/versioned" 7 | typedArgoRollouts "github.com/datawire/argo-rollouts-go-client/pkg/client/clientset/versioned/typed/rollouts/v1alpha1" 8 | ) 9 | 10 | type JoinedClientSetInterface interface { 11 | kubernetes.Interface 12 | argoRollouts.Interface 13 | } 14 | 15 | type joinedClientSetInterface struct { 16 | kubernetes.Interface 17 | ari argoRollouts.Interface 18 | } 19 | 20 | func (j joinedClientSetInterface) ArgoprojV1alpha1() typedArgoRollouts.ArgoprojV1alpha1Interface { 21 | return j.ari.ArgoprojV1alpha1() 22 | } 23 | -------------------------------------------------------------------------------- /pkg/k8sapi/kind.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import "slices" 4 | 5 | type Kind string 6 | 7 | const ( 8 | ServiceKind Kind = "Service" 9 | PodKind Kind = "Pod" 10 | DeploymentKind Kind = "Deployment" 11 | StatefulSetKind Kind = "StatefulSet" 12 | ReplicaSetKind Kind = "ReplicaSet" 13 | RolloutKind Kind = "Rollout" 14 | ) 15 | 16 | type Kinds []Kind 17 | 18 | func (k Kinds) Contains(kind Kind) bool { 19 | return slices.Contains(k, kind) 20 | } 21 | 22 | var ( 23 | KnownKinds = Kinds{ServiceKind, PodKind, DeploymentKind, StatefulSetKind, ReplicaSetKind, RolloutKind} //nolint:gochecknoglobals // constant 24 | KnownWorkloadKinds = Kinds{DeploymentKind, ReplicaSetKind, StatefulSetKind, RolloutKind} //nolint:gochecknoglobals // constant 25 | ) 26 | 27 | func (w Kind) IsValid() bool { 28 | return KnownKinds.Contains(w) 29 | } 30 | -------------------------------------------------------------------------------- /pkg/k8sapi/namespaceid.go: -------------------------------------------------------------------------------- 1 | package k8sapi 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | // GetNamespaceID returns the uuid for a given namespace. If there is an error, it still 10 | // returns a usable ID along with the error. 11 | func GetNamespaceID(ctx context.Context, namespace string) (clusterID string, err error) { 12 | ns, err := GetK8sInterface(ctx).CoreV1().Namespaces().Get(ctx, namespace, v1.GetOptions{}) 13 | if err != nil { 14 | // But still return a usable ID if there's an error. 15 | return "00000000-0000-0000-0000-000000000000", err 16 | } 17 | return string(ns.GetUID()), nil 18 | } 19 | -------------------------------------------------------------------------------- /pkg/log/base_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | func MakeBaseLogger(ctx context.Context, logLevel string) context.Context { 12 | logrusLogger := logrus.StandardLogger() 13 | logrusFormatter := NewFormatter("2006-01-02 15:04:05.0000") 14 | logrusLogger.SetFormatter(logrusFormatter) 15 | 16 | SetLogrusLevel(logrusLogger, logLevel, false) 17 | 18 | logger := dlog.WrapLogrus(logrusLogger) 19 | dlog.SetFallbackLogger(logger) 20 | ctx = dlog.WithLogger(ctx, logger) 21 | return WithLevelSetter(ctx, logrusLogger) 22 | } 23 | -------------------------------------------------------------------------------- /pkg/log/discard.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "log" 7 | 8 | "github.com/datawire/dlib/dlog" 9 | ) 10 | 11 | type discard int 12 | 13 | func (d discard) Helper() { 14 | } 15 | 16 | func (d discard) Log(_ dlog.LogLevel, _ string) { 17 | } 18 | 19 | // We need to implement the UnformattedXXX functions to prevent that 20 | // dlog actually formats the messages prior to discarding them 21 | 22 | func (d discard) UnformattedLog(_ dlog.LogLevel, _ ...any) { 23 | } 24 | 25 | func (d discard) UnformattedLogf(_ dlog.LogLevel, _ string, _ ...any) { 26 | } 27 | 28 | func (d discard) UnformattedLogln(_ dlog.LogLevel, _ ...any) { 29 | } 30 | 31 | func (d discard) StdLogger(_ dlog.LogLevel) *log.Logger { 32 | return log.New(io.Discard, "", 0) 33 | } 34 | 35 | func (d discard) WithField(_ string, _ any) dlog.Logger { 36 | return d 37 | } 38 | 39 | // WithDiscardingLogger returns a context that discards all log output. 40 | func WithDiscardingLogger(ctx context.Context) context.Context { 41 | return dlog.WithLogger(ctx, discard(0)) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/maps/utils.go: -------------------------------------------------------------------------------- 1 | package maps 2 | 3 | import ( 4 | "cmp" 5 | "sort" 6 | ) 7 | 8 | // Copy creates a copy of the given map and returns it. 9 | func Copy[K comparable, V any](a map[K]V) map[K]V { 10 | c := make(map[K]V, len(a)) 11 | for k, v := range a { 12 | c[k] = v 13 | } 14 | return c 15 | } 16 | 17 | // Equal returns true if the two maps contain the exact same set of associations. 18 | func Equal[K comparable, V comparable](a, b map[K]V) bool { 19 | if len(a) != len(b) { 20 | return false 21 | } 22 | for k, v := range a { 23 | if v != b[k] { 24 | return false 25 | } 26 | } 27 | return true 28 | } 29 | 30 | // Merge merges map src into dst, giving the entries in src higher priority. 31 | func Merge[K comparable, V any](dst, src map[K]V) { 32 | for k, v := range src { 33 | dst[k] = v 34 | } 35 | } 36 | 37 | // ToSortedSlice returns a slice of the values in the given map, sorted by that map's keys. 38 | func ToSortedSlice[K cmp.Ordered, V any](m map[K]V) []V { 39 | ns := make([]K, len(m)) 40 | i := 0 41 | for n := range m { 42 | ns[i] = n 43 | i++ 44 | } 45 | sort.Slice(ns, func(i, j int) bool { return ns[i] < ns[j] }) 46 | vs := make([]V, i) 47 | for i, n := range ns { 48 | vs[i] = m[n] 49 | } 50 | return vs 51 | } 52 | -------------------------------------------------------------------------------- /pkg/matcher/header_stringer.go: -------------------------------------------------------------------------------- 1 | package matcher 2 | 3 | import ( 4 | "net/http" 5 | "sort" 6 | "strings" 7 | ) 8 | 9 | // HeaderStringer turns a http.Header into a fmt.Stringer. It is useful when it's desired to defer string formatting of 10 | // the header depending on loglevel, for instance: 11 | // 12 | // dlog.Debugf(c, "Header = %s", HeaderStringer(header)) 13 | // 14 | // would not perform the actual formatting unless the loglevel is DEBUG or higher. 15 | type HeaderStringer http.Header 16 | 17 | // String formats the Header to a readable multi-line string. 18 | func (s HeaderStringer) String() string { 19 | h := http.Header(s) 20 | sb := strings.Builder{} 21 | ks := make([]string, len(h)) 22 | i := 0 23 | for k := range h { 24 | ks[i] = k 25 | i++ 26 | } 27 | sort.Strings(ks) 28 | for i, k := range ks { 29 | if i > 0 { 30 | sb.WriteByte('\n') 31 | } 32 | sb.WriteString(k) 33 | sb.WriteString(": ") 34 | for p, v := range h[k] { 35 | if p > 0 { 36 | sb.WriteByte(',') 37 | } 38 | sb.WriteString(v) 39 | } 40 | } 41 | return sb.String() 42 | } 43 | -------------------------------------------------------------------------------- /pkg/matcher/header_stringer_test.go: -------------------------------------------------------------------------------- 1 | package matcher 2 | 3 | import ( 4 | "net/http" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestStringer_String(t *testing.T) { 10 | newStringer := func(s ...string) HeaderStringer { 11 | h := http.Header{} 12 | for i := 0; i < len(s); i += 2 { 13 | k := s[i] 14 | vs := strings.Split(s[i+1], ";") // THe semicolon is deliberate to ensure that comma is the result of formatting 15 | h.Set(k, vs[0]) 16 | for i := 1; i < len(vs); i++ { 17 | h.Add(k, vs[i]) 18 | } 19 | } 20 | return HeaderStringer(h) 21 | } 22 | 23 | tests := []struct { 24 | name string 25 | s HeaderStringer 26 | want string 27 | }{ 28 | { 29 | "one header, single value", 30 | newStringer("hdr-one", "val"), 31 | "Hdr-One: val", 32 | }, 33 | { 34 | "one header, multiple values", 35 | newStringer("hdr-one", "val1;val2;val3"), 36 | "Hdr-One: val1,val2,val3", 37 | }, 38 | { 39 | "multiple headers, single value", 40 | newStringer("hdr-one", "val1;val2", "hdr-two", "the value"), 41 | "Hdr-One: val1,val2\nHdr-Two: the value", 42 | }, 43 | } 44 | for _, tt := range tests { 45 | t.Run(tt.name, func(t *testing.T) { 46 | if got := tt.s.String(); got != tt.want { 47 | t.Errorf("String() = %q, want %q", got, tt.want) 48 | } 49 | }) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pkg/pprof/pprof.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | _ "net/http/pprof" 8 | 9 | "github.com/datawire/dlib/dhttp" 10 | ) 11 | 12 | func PprofServer(ctx context.Context, port uint16) error { 13 | sc := dhttp.ServerConfig{Handler: http.DefaultServeMux} 14 | return sc.ListenAndServe(ctx, fmt.Sprintf("localhost:%d", port)) 15 | } 16 | -------------------------------------------------------------------------------- /pkg/proc/docker_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package proc 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "os" 9 | 10 | "github.com/telepresenceio/telepresence/v2/pkg/routing" 11 | ) 12 | 13 | var runningInContainer bool //nolint:gochecknoglobals // this is a constant 14 | 15 | func init() { 16 | _, err := os.Stat("/.dockerenv") 17 | runningInContainer = err == nil 18 | } 19 | 20 | // RunningInContainer returns true if the current process runs from inside a docker container. 21 | func RunningInContainer() bool { 22 | return runningInContainer 23 | } 24 | 25 | // SetRunningInContainer forces the runningInContainer state. 26 | func SetRunningInContainer(flag bool) { 27 | runningInContainer = flag 28 | } 29 | 30 | func AppendOSSpecificContainerOpts(ctx context.Context, opts []string) ([]string, error) { 31 | if RunningInWSL() { 32 | // Using host.docker.internal:host-gateway won't work for the kubeauth process, because Windows Docker Desktop 33 | // will assign the IP of the Windows host, not the host from where this process was started (the Linux host). 34 | // We'll reach that using the gateway of the default host. 35 | r, err := routing.DefaultRoute(ctx) 36 | if err != nil { 37 | return opts, err 38 | } 39 | opts = append(opts, "-e", fmt.Sprintf("TELEPRESENCE_KUBEAUTH_HOST=%s", r.LocalIP)) 40 | } 41 | return opts, nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/proc/docker_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package proc 4 | 5 | import "context" 6 | 7 | // RunningInContainer returns true if the current process runs from inside a docker container. 8 | func RunningInContainer() bool { 9 | return false 10 | } 11 | 12 | func SetRunningInContainer(_ bool) { 13 | } 14 | 15 | func AppendOSSpecificContainerOpts(_ context.Context, opts []string) ([]string, error) { 16 | return opts, nil 17 | } 18 | -------------------------------------------------------------------------------- /pkg/proc/wsl_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | 3 | package proc 4 | 5 | import ( 6 | "os" 7 | "strings" 8 | ) 9 | 10 | var runningInWSL bool //nolint:gochecknoglobals // this is a constant 11 | 12 | func init() { 13 | pv, err := os.ReadFile("/proc/version") 14 | if err == nil { 15 | sv := strings.ToLower(string(pv)) 16 | runningInWSL = strings.Contains(sv, "microsoft") && strings.Contains(sv, "wsl") 17 | } 18 | } 19 | 20 | // RunningInWSL returns true if the current process is a Linux running under WSL. 21 | func RunningInWSL() bool { 22 | return runningInWSL 23 | } 24 | -------------------------------------------------------------------------------- /pkg/proc/wsl_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package proc 4 | 5 | // RunningInWSL returns true if the current process is a Linux running under WSL. 6 | func RunningInWSL() bool { 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /pkg/routing/routing_test.go: -------------------------------------------------------------------------------- 1 | package routing 2 | 3 | import ( 4 | "net/netip" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | func TestGetRoutingTable_defaultRoute(t *testing.T) { 13 | ctx := dlog.NewTestContext(t, true) 14 | rt, err := GetRoutingTable(ctx) 15 | assert.NoError(t, err) 16 | var dflt *Route 17 | for _, r := range rt { 18 | if r.Default { 19 | dflt = r 20 | break 21 | } 22 | } 23 | assert.NotNil(t, dflt) 24 | assert.NotEqual(t, netip.IPv4Unspecified(), dflt.Gateway) 25 | assert.NotEqual(t, netip.IPv6Unspecified(), dflt.Gateway) 26 | } 27 | 28 | func TestGetRoutingTable(t *testing.T) { 29 | ctx := dlog.NewTestContext(t, true) 30 | rt, err := GetRoutingTable(ctx) 31 | assert.NoError(t, err) 32 | assert.NotEmpty(t, rt) 33 | for _, r := range rt { 34 | assert.True(t, r.LocalIP.IsValid()) 35 | assert.NotNil(t, r.Interface) 36 | assert.True(t, r.RoutedNet.IsValid()) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pkg/slice/contains.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | // AppendUnique appends all elements that are not already present in dest to dest 4 | // and returns the result. 5 | func AppendUnique[E comparable](dest []E, src ...E) []E { 6 | for _, v := range src { 7 | if !Contains(dest, v) { 8 | dest = append(dest, v) 9 | } 10 | } 11 | return dest 12 | } 13 | 14 | // Contains returns true if the given slice contains the given element. 15 | func Contains[E comparable](vs []E, e E) bool { 16 | for _, v := range vs { 17 | if e == v { 18 | return true 19 | } 20 | } 21 | return false 22 | } 23 | 24 | // ContainsAll returns true if the first slice contains all elements in the second slice. 25 | func ContainsAll[E comparable](vs []E, es []E) bool { 26 | for _, e := range es { 27 | if !Contains(vs, e) { 28 | return false 29 | } 30 | } 31 | return true 32 | } 33 | 34 | // ContainsAny returns true if the first slice contains at least one of the elements in the second slice. 35 | func ContainsAny[E comparable](vs []E, es []E) bool { 36 | for _, e := range es { 37 | if Contains(vs, e) { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | -------------------------------------------------------------------------------- /pkg/slice/csv.go: -------------------------------------------------------------------------------- 1 | package slice 2 | 3 | import ( 4 | "bytes" 5 | "encoding/csv" 6 | "strings" 7 | ) 8 | 9 | // AsCSV returns the string slice encoded by a csv.NewWriter. 10 | func AsCSV(vs []string) string { 11 | b := &bytes.Buffer{} 12 | w := csv.NewWriter(b) 13 | if err := w.Write(vs); err != nil { 14 | // The underlying bytes.Buffer should never error. 15 | panic(err) 16 | } 17 | w.Flush() 18 | return strings.TrimSuffix(b.String(), "\n") 19 | } 20 | -------------------------------------------------------------------------------- /pkg/tunnel/client_stream.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | type GRPCClientStream interface { 11 | GRPCStream 12 | CloseSend() error 13 | } 14 | 15 | func NewClientStream(ctx context.Context, tag Tag, grpcStream GRPCClientStream, id ConnID, sessionID SessionID, callDelay, dialTimeout time.Duration) (Stream, error) { 16 | s := &clientStream{stream: newStream(tag, grpcStream)} 17 | s.id = id 18 | s.roundtripLatency = callDelay 19 | s.dialTimeout = dialTimeout 20 | s.sessionID = sessionID 21 | 22 | if err := s.Send(ctx, StreamInfoMessage(id, sessionID, callDelay, dialTimeout)); err != nil { 23 | _ = s.CloseSend(ctx) 24 | return nil, err 25 | } 26 | m, err := s.Receive(ctx) 27 | if err != nil { 28 | _ = s.CloseSend(ctx) 29 | return nil, fmt.Errorf("failed to read initial StreamOK message: %w", err) 30 | } 31 | if m.Code() != streamOK { 32 | _ = s.CloseSend(ctx) 33 | return nil, errors.New("initial message was not StreamOK") 34 | } 35 | s.peerVersion = getVersion(m) 36 | return s, nil 37 | } 38 | 39 | type clientStream struct { 40 | stream 41 | } 42 | 43 | func (s *clientStream) CloseSend(_ context.Context) error { 44 | return s.grpcStream.(GRPCClientStream).CloseSend() 45 | } 46 | -------------------------------------------------------------------------------- /pkg/tunnel/context.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import "context" 4 | 5 | type poolKey struct{} 6 | 7 | // WithPool returns a context with the given Pool. 8 | func WithPool(ctx context.Context, pool *Pool) context.Context { 9 | return context.WithValue(ctx, poolKey{}, pool) 10 | } 11 | 12 | func GetPool(ctx context.Context) *Pool { 13 | pool, ok := ctx.Value(poolKey{}).(*Pool) 14 | if !ok { 15 | return nil 16 | } 17 | return pool 18 | } 19 | -------------------------------------------------------------------------------- /pkg/tunnel/probe.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | type CounterProbe struct { 8 | name string 9 | value uint64 10 | } 11 | 12 | func NewCounterProbe(name string) *CounterProbe { 13 | return &CounterProbe{name: name} 14 | } 15 | 16 | func (p *CounterProbe) Increment(v uint64) { 17 | atomic.AddUint64(&p.value, v) 18 | } 19 | 20 | func (p *CounterProbe) GetName() string { 21 | return p.name 22 | } 23 | 24 | func (p *CounterProbe) GetValue() uint64 { 25 | return atomic.LoadUint64(&p.value) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/tunnel/server_stream.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | func NewServerStream(ctx context.Context, tag Tag, grpcStream GRPCStream) (Stream, error) { 10 | s := &stream{tag: tag, grpcStream: grpcStream, syncRatio: 8, ackWindow: 1} 11 | m, err := s.Receive(ctx) 12 | if err != nil { 13 | return nil, fmt.Errorf("failed to read initial StreamInfo message: %w", err) 14 | } 15 | if m.Code() != streamInfo { 16 | return nil, errors.New("initial message was not StreamInfo") 17 | } 18 | if err = setConnectInfo(m, s); err != nil { 19 | return nil, fmt.Errorf("failed to parse StreamInfo message: %w", err) 20 | } 21 | if err = s.Send(ctx, StreamOKMessage()); err != nil { 22 | return nil, err 23 | } 24 | return s, nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "os" 5 | "runtime/debug" 6 | "strings" 7 | 8 | "github.com/blang/semver/v4" 9 | ) 10 | 11 | // Version is a "vSEMVER" string, and is either populated at build-time using `--ldflags -X`, or at 12 | // init()-time by inspecting the binary's own debug info. 13 | var ( 14 | Version string //nolint:gochecknoglobals // constant 15 | Structured semver.Version //nolint:gochecknoglobals // constant 16 | ) 17 | 18 | func init() { 19 | // Prefer version number inserted at build using --ldflags, but if it's not set... 20 | Version, Structured = Init(Version, "TELEPRESENCE_VERSION") 21 | } 22 | 23 | func Init(s, envKey string) (string, semver.Version) { 24 | if s == "" { 25 | if info, ok := debug.ReadBuildInfo(); ok { 26 | // Fall back to version info from "go get" 27 | s = info.Main.Version 28 | } 29 | if s == "" { 30 | if s = os.Getenv(envKey); s == "" { 31 | s = "0.0.0-unknown" 32 | } 33 | } 34 | } 35 | if s == "(devel)" { 36 | s = "0.0.0-devel" 37 | } 38 | s = strings.TrimPrefix(s, "v") 39 | sv, err := semver.Parse(s) 40 | if err != nil { 41 | panic(err) 42 | } 43 | return "v" + s, sv 44 | } 45 | 46 | func GetExecutable() (string, error) { 47 | executable, err := os.Executable() 48 | if err != nil { 49 | return "", err 50 | } 51 | return executable, nil 52 | } 53 | -------------------------------------------------------------------------------- /pkg/vif/device_unix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package vif 5 | 6 | import ( 7 | "context" 8 | "net/netip" 9 | ) 10 | 11 | func (d *device) setDNS(context.Context, string, netip.Addr, []string) (err error) { 12 | // DNS is configured by other means than through the actual device 13 | return nil 14 | } 15 | -------------------------------------------------------------------------------- /pkg/vif/logging.go: -------------------------------------------------------------------------------- 1 | package vif 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "gvisor.dev/gvisor/pkg/log" 8 | 9 | "github.com/datawire/dlib/dlog" 10 | ) 11 | 12 | type dlogEmitter struct { 13 | context.Context 14 | } 15 | 16 | func (l dlogEmitter) Emit(_ int, level log.Level, _ time.Time, format string, v ...interface{}) { //nolint:goprintffuncname // not our API 17 | switch level { 18 | case log.Debug: 19 | dlog.Debugf(l, format, v...) 20 | case log.Info: 21 | dlog.Infof(l, format, v...) 22 | case log.Warning: 23 | dlog.Warnf(l, format, v...) 24 | } 25 | } 26 | 27 | func InitLogger(ctx context.Context) { 28 | log.SetTarget(&dlogEmitter{Context: ctx}) 29 | var gl log.Level 30 | switch dlog.MaxLogLevel(ctx) { 31 | case dlog.LogLevelInfo: 32 | gl = log.Info 33 | case dlog.LogLevelDebug, dlog.LogLevelTrace: 34 | gl = log.Debug 35 | default: 36 | gl = log.Warning 37 | } 38 | log.SetLevel(gl) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/vif/testdata/router/.gitignore: -------------------------------------------------------------------------------- 1 | router 2 | -------------------------------------------------------------------------------- /pkg/workload/util.go: -------------------------------------------------------------------------------- 1 | package workload 2 | 3 | import ( 4 | "k8s.io/apimachinery/pkg/runtime" 5 | 6 | "github.com/telepresenceio/telepresence/rpc/v2/manager" 7 | "github.com/telepresenceio/telepresence/v2/pkg/k8sapi" 8 | ) 9 | 10 | func FromAny(obj any) (k8sapi.Workload, bool) { 11 | if ro, ok := obj.(runtime.Object); ok { 12 | if wl, err := k8sapi.WrapWorkload(ro); err == nil { 13 | return wl, true 14 | } 15 | } 16 | return nil, false 17 | } 18 | 19 | func RpcKind(s k8sapi.Kind) manager.WorkloadInfo_Kind { 20 | switch s { 21 | case k8sapi.DeploymentKind: 22 | return manager.WorkloadInfo_DEPLOYMENT 23 | case k8sapi.ReplicaSetKind: 24 | return manager.WorkloadInfo_REPLICASET 25 | case k8sapi.StatefulSetKind: 26 | return manager.WorkloadInfo_STATEFULSET 27 | case k8sapi.RolloutKind: 28 | return manager.WorkloadInfo_ROLLOUT 29 | default: 30 | return manager.WorkloadInfo_UNSPECIFIED 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rpc/agent/agent.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // The "agent" package describes the server implemented by the 4 | // in-cluster Agent 5 | package telepresence.agent; 6 | 7 | import "google/protobuf/empty.proto"; 8 | import "manager/manager.proto"; 9 | 10 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/agent"; 11 | 12 | service Agent { 13 | rpc Tunnel(stream manager.TunnelMessage) returns (stream manager.TunnelMessage); 14 | 15 | // Version returns the version information of the Manager. 16 | rpc Version(google.protobuf.Empty) returns (manager.VersionInfo2); 17 | 18 | // WatchDial makes it possible for the client side to receive DialRequests 19 | // from the traffic-agent. Requests are sent when an intercepted agent needs 20 | // a Tunnel to the Telepresence client on the workstation. The receiver of 21 | // the request dials a connection and responds with the needed Tunnel. 22 | rpc WatchDial(manager.SessionInfo) returns (stream manager.DialRequest); 23 | } -------------------------------------------------------------------------------- /rpc/authenticator/authenticator.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package telepresence.authenticator; 4 | 5 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/authenticator"; 6 | 7 | service Authenticator { 8 | rpc GetContextExecCredentials(GetContextExecCredentialsRequest) returns (GetContextExecCredentialsResponse); 9 | } 10 | 11 | message GetContextExecCredentialsRequest { 12 | string context_name = 1; 13 | } 14 | 15 | message GetContextExecCredentialsResponse { 16 | bytes raw_credentials = 1; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /rpc/common/errors.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package telepresence.common; 3 | 4 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/common"; 5 | 6 | message Result { 7 | enum ErrorCategory { 8 | UNSPECIFIED = 0; // No error 9 | USER = 1; 10 | CONFIG = 2; 11 | NO_DAEMON_LOGS = 3; 12 | UNKNOWN = 4; 13 | } 14 | 15 | bytes data = 1; 16 | ErrorCategory error_category = 2; 17 | } 18 | 19 | // InterceptError is a common error type used by the intercept call family (add, 20 | // remove, list, available). 21 | enum InterceptError { 22 | UNSPECIFIED = 0; // no error 23 | INTERNAL = 1; 24 | NO_CONNECTION = 2; // Have not made the .Connect RPC call (or it errored) 25 | NO_TRAFFIC_MANAGER = 3; 26 | TRAFFIC_MANAGER_CONNECTING = 4; 27 | TRAFFIC_MANAGER_ERROR = 5; 28 | ALREADY_EXISTS = 6; 29 | NAMESPACE_AMBIGUITY = 17; 30 | LOCAL_TARGET_IN_USE = 7; 31 | NO_ACCEPTABLE_WORKLOAD = 8; 32 | AMBIGUOUS_MATCH = 9; 33 | FAILED_TO_ESTABLISH = 10; 34 | UNSUPPORTED_WORKLOAD = 11; 35 | MISCONFIGURED_WORKLOAD = 14; 36 | NOT_FOUND = 12; 37 | MOUNT_POINT_BUSY = 13; 38 | UNKNOWN_FLAG = 15; 39 | EXEC_CMD = 16; // External exec command failed 40 | } 41 | -------------------------------------------------------------------------------- /rpc/common/version.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package telepresence.common; 3 | 4 | option go_package = "github.com/telepresenceio/telepresence/rpc/v2/common"; 5 | 6 | // VersionInfo is the type that both `telepresence daemon` (the super-user 7 | // daemon) and `telepresence conector` (the normal-user daemon) use 8 | // when reporting their version to the user-facing CLI. 9 | message VersionInfo { 10 | int32 api_version = 1; 11 | 12 | // Version is a "vSEMVER" string of the product version number. 13 | string version = 2; 14 | 15 | // Executable is the path to the executable for the process. 16 | string executable = 3; 17 | 18 | // Name of the process (Client, User Daemon, Root Daemon, Traffic Manager) 19 | string name = 4; 20 | } 21 | -------------------------------------------------------------------------------- /rpc/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/rpc/v2 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | google.golang.org/grpc v1.72.2 7 | google.golang.org/protobuf v1.36.6 8 | ) 9 | 10 | require ( 11 | golang.org/x/net v0.40.0 // indirect 12 | golang.org/x/sys v0.33.0 // indirect 13 | golang.org/x/text v0.25.0 // indirect 14 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect 15 | ) 16 | -------------------------------------------------------------------------------- /test-infra/aws-okd/dns/.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | .terraform* 3 | terraform.tfvars 4 | terraform.tfstate 5 | -------------------------------------------------------------------------------- /test-infra/aws-okd/dns/dns.tf: -------------------------------------------------------------------------------- 1 | variable "parent_domain" { 2 | type = string 3 | description = "The name of the parent domain" 4 | } 5 | 6 | variable "child_subdomain" { 7 | type = string 8 | description = "The name of the child domain" 9 | } 10 | 11 | variable "child_subdomain_comment" { 12 | type = string 13 | description = "The comment of the child domain" 14 | } 15 | 16 | variable "aws_region" { 17 | type = string 18 | description = "The region to create the resources in" 19 | } 20 | 21 | terraform { 22 | required_providers { 23 | aws = { 24 | source = "hashicorp/aws", 25 | version = "~> 3.20" 26 | } 27 | } 28 | } 29 | 30 | provider "aws" { 31 | region = var.aws_region 32 | } 33 | 34 | data "aws_route53_zone" "parent_dns_zone" { 35 | name = var.parent_domain 36 | } 37 | 38 | 39 | resource "aws_route53_zone" "child_dns_zone" { 40 | name = "${var.child_subdomain}.${data.aws_route53_zone.parent_dns_zone.name}" 41 | comment = var.child_subdomain_comment 42 | } 43 | 44 | resource "aws_route53_record" "child_dns_route" { 45 | zone_id = data.aws_route53_zone.parent_dns_zone.id 46 | name = var.child_subdomain 47 | type = "NS" 48 | ttl = 3600 49 | records = aws_route53_zone.child_dns_zone.name_servers 50 | } 51 | -------------------------------------------------------------------------------- /test-infra/aws-vpn/.gitignore: -------------------------------------------------------------------------------- 1 | certs/* 2 | *.tfstate 3 | *.backup 4 | *.tfvars 5 | .terraform 6 | .terraform.* 7 | -------------------------------------------------------------------------------- /test-infra/aws-vpn/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | aws = { 4 | source = "hashicorp/aws", 5 | version = "~> 3.20" 6 | } 7 | } 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | -------------------------------------------------------------------------------- /tools/src/go-mkopensource/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "github.com/datawire/go-mkopensource/cmd/go-mkopensource" 7 | -------------------------------------------------------------------------------- /tools/src/gosimports/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.0 6 | 7 | require github.com/rinchsan/gosimports v0.3.8 8 | 9 | require ( 10 | golang.org/x/mod v0.24.0 // indirect 11 | golang.org/x/sys v0.33.0 // indirect 12 | golang.org/x/tools v0.33.0 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /tools/src/gosimports/go.sum: -------------------------------------------------------------------------------- 1 | github.com/rinchsan/gosimports v0.3.8 h1:X4Pb9yFf6teHvogorT04yK/0W2Df7eHO79biCcYrA4c= 2 | github.com/rinchsan/gosimports v0.3.8/go.mod h1:t0567k69sUHjLvJMPDsV31THZC+8UIbY1oL7NW+0I2c= 3 | golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= 4 | golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 5 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 6 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 7 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 8 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 9 | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 10 | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 11 | golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= 12 | golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= 13 | golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= 14 | golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= 15 | golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= 16 | golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 17 | -------------------------------------------------------------------------------- /tools/src/gosimports/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "github.com/rinchsan/gosimports/cmd/gosimports" 7 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go-grpc/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.22 4 | 5 | toolchain go1.24.0 6 | 7 | require google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 8 | 9 | require google.golang.org/protobuf v1.36.6 // indirect 10 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go-grpc/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "google.golang.org/grpc/cmd/protoc-gen-go-grpc" 7 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.22.0 4 | 5 | toolchain go1.22.3 6 | 7 | require google.golang.org/protobuf v1.36.6 8 | 9 | require github.com/google/go-cmp v0.7.0 // indirect 10 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 2 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 3 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 4 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 5 | -------------------------------------------------------------------------------- /tools/src/protoc-gen-go/pin.go: -------------------------------------------------------------------------------- 1 | //go:build pin 2 | // +build pin 3 | 4 | package ignore 5 | 6 | import "google.golang.org/protobuf/cmd/protoc-gen-go" 7 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/tools/src/relnotesgen 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/blang/semver/v4 v4.0.0 7 | sigs.k8s.io/yaml v1.4.0 8 | ) 9 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/go.sum: -------------------------------------------------------------------------------- 1 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 2 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 3 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 4 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 5 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 6 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 7 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | _ "embed" 5 | "flag" 6 | "log" 7 | 8 | "github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes" 9 | ) 10 | 11 | func main() { 12 | var input string 13 | var mdx bool 14 | var variables bool 15 | flag.StringVar(&input, "input", "", "input file") 16 | flag.BoolVar(&mdx, "mdx", false, "generate mdx") 17 | flag.BoolVar(&variables, "variables", false, "generate mdx") 18 | flag.Parse() 19 | var err error 20 | if variables { 21 | err = relnotes.MakeVariables(input) 22 | } else { 23 | err = relnotes.MakeReleaseNotes(input, mdx) 24 | } 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/relnotes/relnotes.gomd: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes.ChangeLog*/}} 2 | [comment]: # (Code generated by relnotesgen. DO NOT EDIT.) 3 | 4 | {{- $styles := .Styles -}} 5 | {{- $iconFormat := "images/%s.png" }} 6 | # Telepresence Release Notes 7 | {{- range .Items }} 8 | {{- if not (eq .DateString "(SUPERSEDED)")}} 9 | {{ if .Version}}## Version {{.Version}}{{- with .Date}} ({{- (.).UTC.Format "January _2"}}){{- end}}{{- end}} 10 | {{- end -}} 11 | {{- range .Notes }} 12 | ##
{{.Type}}
13 | {{- if .HRef}}[{{.Title}}]({{.HRef}}) 14 | {{- else if .Docs}}[{{.Title}}]({{.Docs}}) 15 | {{- else}}{{.Title}} 16 | {{- end -}}
17 |
18 | {{- /* An empty line is crucial here to ensure that the Body is treated as markdown */}} 19 | 20 | {{.Body}} 21 |
22 | {{ end }} 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /tools/src/relnotesgen/relnotes/relnotes.gomdx: -------------------------------------------------------------------------------- 1 | {{- /*gotype: github.com/telepresenceio/telepresence/tools/src/relnotesgen/relnotes.ChangeLog*/ -}} 2 | --- 3 | title: Release Notes 4 | --- 5 | 6 | import { Note, Title, Body } from '@site/src/components/ReleaseNotes' 7 | 8 | [comment]: # (Code generated by relnotesgen. DO NOT EDIT.) 9 | {{ $styles := .Styles -}} 10 | {{- $iconFormat := "images/%s.png" }} 11 | # Telepresence Release Notes 12 | {{- range .Items }} 13 | {{- if not (eq .DateString "(SUPERSEDED)")}} 14 | {{ if .Version}}## Version {{.Version}}{{- with .Date}} ({{- (.).UTC.Format "January _2"}}){{- end}}{{- end}} 15 | {{- end -}} 16 | {{- range .Notes }} 17 | 18 | {{.Title}} 19 | 20 | {{.Body}} 21 | 22 | {{- if .Image}} 23 | 24 | {{- end}} 25 | 26 | {{- end }} 27 | {{- end }} 28 | -------------------------------------------------------------------------------- /tools/src/test-report/go.mod: -------------------------------------------------------------------------------- 1 | module local 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.24.0 6 | 7 | require ( 8 | github.com/fatih/color v1.18.0 9 | github.com/vbauerster/mpb/v8 v8.10.1 10 | ) 11 | 12 | require ( 13 | github.com/VividCortex/ewma v1.2.0 // indirect 14 | github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect 15 | github.com/mattn/go-colorable v0.1.14 // indirect 16 | github.com/mattn/go-isatty v0.0.20 // indirect 17 | github.com/mattn/go-runewidth v0.0.16 // indirect 18 | github.com/rivo/uniseg v0.4.7 // indirect 19 | golang.org/x/sys v0.33.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /tools/src/y2j/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/telepresenceio/telepresence/tools/src/y2j 2 | 3 | go 1.24 4 | 5 | require sigs.k8s.io/yaml v1.4.0 6 | -------------------------------------------------------------------------------- /tools/src/y2j/go.sum: -------------------------------------------------------------------------------- 1 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 2 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 3 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 4 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 5 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 6 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 7 | -------------------------------------------------------------------------------- /tools/src/y2j/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "os" 7 | 8 | "sigs.k8s.io/yaml" 9 | ) 10 | 11 | func main() { 12 | data, err := io.ReadAll(os.Stdin) 13 | if err == nil { 14 | data, err = yaml.YAMLToJSON(data) 15 | if err == nil { 16 | _, err = os.Stdout.Write(data) 17 | } 18 | } 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | } 23 | --------------------------------------------------------------------------------