From b8a0ee2a593dcf0d4b0eed716635913b81e90856 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Fri, 18 Jul 2025 15:24:27 -0400 Subject: [PATCH 01/16] PID and readyz changes for tbot --- integrations/terraform-mwi/go.sum | 14 + integrations/terraform/go.sum | 14 + lib/autoupdate/agent/installer.go | 9 +- lib/autoupdate/agent/installer_test.go | 26 +- lib/autoupdate/agent/internal/unversioned.go | 33 + .../agent/internal/unversioned_test.go | 190 ++++ lib/autoupdate/agent/process.go | 101 +- lib/autoupdate/agent/setup.go | 319 ++++--- lib/autoupdate/agent/setup_test.go | 384 +++++--- ...PS_and_Enterprise_flags_tbot=false.golden} | 0 ...FIPS_and_Enterprise_flags_tbot=true.golden | 12 + ... agpl_requires_base_URL_tbot=false.golden} | 0 ...> agpl_requires_base_URL_tbot=true.golden} | 0 ...ion_kept_for_validation_tbot=false.golden} | 0 ...rsion_kept_for_validation_tbot=true.golden | 13 + ...sion_removed_on_install_tbot=false.golden} | 0 ...rsion_removed_on_install_tbot=true.golden} | 0 ...> config_does_not_exist_tbot=false.golden} | 0 ...=> config_does_not_exist_tbot=true.golden} | 0 ...den => config_from_file_tbot=false.golden} | 0 .../config_from_file_tbot=true.golden | 15 + ...den => config_from_user_tbot=false.golden} | 0 .../config_from_user_tbot=true.golden | 15 + ...skip.golden => defaults_tbot=false.golden} | 0 .../defaults_tbot=true.golden | 13 + ....golden => insecure_URL_tbot=false.golden} | 0 .../insecure_URL_tbot=true.golden | 11 + ...golden => install_error_tbot=false.golden} | 0 .../install_error_tbot=true.golden | 10 + ...stall_selinux_from_file_tbot=false.golden} | 0 ...nstall_selinux_from_file_tbot=true.golden} | 0 ...nstall_selinux_from_user_tbot=false.golden | 14 + ...install_selinux_from_user_tbot=true.golden | 14 + ...den => invalid_metadata_tbot=false.golden} | 0 .../invalid_metadata_tbot=true.golden | 10 + ...en => no_need_to_reload_tbot=false.golden} | 0 ...den => no_need_to_reload_tbot=true.golden} | 0 .../not_started_or_enabled_tbot=false.golden | 11 + .../not_started_or_enabled_tbot=true.golden | 11 + .../override_skip_tbot=false.golden | 13 + .../override_skip_tbot=true.golden | 13 + ...fails_already_installed_tbot=false.golden} | 0 ...p_fails_already_installed_tbot=true.golden | 10 + .../setup_fails_tbot=false.golden | 10 + .../setup_fails_tbot=true.golden | 10 + ...ersion_already_installed_tbot=false.golden | 11 + ...version_already_installed_tbot=true.golden | 11 + ...PS_and_Enterprise_flags_tbot=false.golden} | 0 ...FIPS_and_Enterprise_flags_tbot=true.golden | 22 + ... agpl_requires_base_URL_tbot=false.golden} | 0 .../agpl_requires_base_URL_tbot=true.golden | 18 + ...ackup_version_is_linked_tbot=false.golden} | 0 ...backup_version_is_linked_tbot=true.golden} | 0 ...ion_kept_when_no_change_tbot=false.golden} | 0 ...rsion_kept_when_no_change_tbot=true.golden | 13 + ...sion_removed_on_install_tbot=false.golden} | 0 ...ersion_removed_on_install_tbot=true.golden | 19 + ....golden => insecure_URL_tbot=false.golden} | 0 .../insecure_URL_tbot=true.golden | 11 + ...golden => install_error_tbot=false.golden} | 0 .../install_error_tbot=true.golden | 16 + ...den => invalid_metadata_tbot=false.golden} | 0 .../invalid_metadata_tbot=true.golden | 10 + ...sing_path_during_window_tbot=false.golden} | 0 ...issing_path_during_window_tbot=true.golden | 12 + ...olden => pinned_version_tbot=false.golden} | 0 .../pinned_version_tbot=true.golden | 13 + ...s.golden => setup_fails_tbot=false.golden} | 0 .../setup_fails_tbot=true.golden | 21 + ....golden => skip_version_tbot=false.golden} | 0 .../skip_version_tbot=true.golden | 15 + ..._disabled_during_window_tbot=false.golden} | 0 ...s_disabled_during_window_tbot=true.golden} | 0 ...sabled_outside_of_window_tbot=false.golden | 12 + ...isabled_outside_of_window_tbot=true.golden | 12 + ...s_enabled_during_window_tbot=false.golden} | 0 ...es_enabled_during_window_tbot=true.golden} | 0 ..._not_started_or_enabled_tbot=false.golden} | 0 ...w,_not_started_or_enabled_tbot=true.golden | 20 + .../updates_enabled_now_tbot=false.golden | 20 + .../updates_enabled_now_tbot=true.golden | 20 + ...abled_outside_of_window_tbot=false.golden} | 0 ...enabled_outside_of_window_tbot=true.golden | 12 + ...ady_installed_in_window_tbot=false.golden} | 0 ...eady_installed_in_window_tbot=true.golden} | 0 ...talled_outside_of_window_tbot=false.golden | 11 + ...stalled_outside_of_window_tbot=true.golden | 11 + ...ersion_detects_as_linked_tbot=false.golden | 19 + ...version_detects_as_linked_tbot=true.golden | 19 + .../no_namespace/needrestart.golden | 1 + .../test_namespace/needrestart.golden | 1 + .../deprecated.golden | 6 + .../dropin.golden | 6 + .../needrestart.golden | 1 + .../service.golden | 9 + .../timer.golden | 13 + .../test_with_custom_tbot/deprecated.golden | 6 + .../test_with_custom_tbot/dropin.golden | 6 + .../test_with_custom_tbot/needrestart.golden | 1 + .../test_with_custom_tbot/service.golden | 9 + .../test_with_custom_tbot/timer.golden | 13 + .../TestWriteTbotService/custom.golden | 22 + .../TestWriteTbotService/custom_suffix.golden | 22 + .../TestWriteTbotService/default.golden | 22 + lib/autoupdate/agent/updater.go | 325 +++++-- lib/autoupdate/agent/updater_test.go | 883 ++++++++++-------- lib/service/service.go | 35 +- lib/tbot/cli/start_legacy.go | 12 + lib/tbot/config/config.go | 7 + .../tbot/config/systemd}/systemd.tmpl | 2 +- lib/tbot/config/systemd/template.go | 40 + lib/tbot/internal/diagnostics/service.go | 33 +- lib/tbot/readyz/readyz.go | 5 + lib/tbot/readyz/readyz_test.go | 4 + lib/tbot/tbot.go | 20 + lib/utils/pid.go | 61 ++ tool/tbot/systemd.go | 20 +- .../succeeds_prexisting_with_force.golden | 2 +- .../success_-_defaults.golden | 2 +- .../success_-_defaults_and_dry_run.golden | 2 +- .../success_-_overrides.golden | 2 +- tool/teleport-update/main.go | 10 +- 122 files changed, 2473 insertions(+), 773 deletions(-) create mode 100644 lib/autoupdate/agent/internal/unversioned.go create mode 100644 lib/autoupdate/agent/internal/unversioned_test.go rename lib/autoupdate/agent/testdata/TestUpdater_Install/{FIPS_and_Enterprise_flags.golden => FIPS_and_Enterprise_flags_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{agpl_requires_base_URL.golden => agpl_requires_base_URL_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_error.golden => agpl_requires_base_URL_tbot=true.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{backup_version_kept_for_validation.golden => backup_version_kept_for_validation_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{backup_version_removed_on_install.golden => backup_version_removed_on_install_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{defaults.golden => backup_version_removed_on_install_tbot=true.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_does_not_exist.golden => config_does_not_exist_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{no_need_to_reload.golden => config_does_not_exist_tbot=true.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_from_file.golden => config_from_file_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_from_user.golden => config_from_user_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{override_skip.golden => defaults_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{insecure_URL.golden => insecure_URL_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{setup_fails.golden => install_error_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_selinux_from_file.golden => install_selinux_from_file_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_selinux_from_user.golden => install_selinux_from_file_tbot=true.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{invalid_metadata.golden => invalid_metadata_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{not_started_or_enabled.golden => no_need_to_reload_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{version_already_installed.golden => no_need_to_reload_tbot=true.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{setup_fails_already_installed.golden => setup_fails_already_installed_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{FIPS_and_Enterprise_flags.golden => FIPS_and_Enterprise_flags_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{agpl_requires_base_URL.golden => agpl_requires_base_URL_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_is_linked.golden => backup_version_is_linked_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_removed_on_install.golden => backup_version_is_linked_tbot=true.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_kept_when_no_change.golden => backup_version_kept_when_no_change_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{version_detects_as_linked.golden => backup_version_removed_on_install_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{insecure_URL.golden => insecure_URL_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{install_error.golden => install_error_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{invalid_metadata.golden => invalid_metadata_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{missing_path_during_window.golden => missing_path_during_window_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{pinned_version.golden => pinned_version_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{setup_fails.golden => setup_fails_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{skip_version.golden => skip_version_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_disabled_during_window.golden => updates_disabled_during_window_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_disabled_outside_of_window.golden => updates_disabled_during_window_tbot=true.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_during_window.golden => updates_enabled_during_window_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_now,_not_started_or_enabled.golden => updates_enabled_during_window_tbot=true.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_now.golden => updates_enabled_now,_not_started_or_enabled_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_outside_of_window.golden => updates_enabled_outside_of_window_tbot=false.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{version_already_installed_in_window.golden => version_already_installed_in_window_tbot=false.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{version_already_installed_outside_of_window.golden => version_already_installed_in_window_tbot=true.golden} (100%) create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden create mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden create mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden rename {tool/tbot => lib/tbot/config/systemd}/systemd.tmpl (71%) create mode 100644 lib/tbot/config/systemd/template.go create mode 100644 lib/utils/pid.go diff --git a/integrations/terraform-mwi/go.sum b/integrations/terraform-mwi/go.sum index ebf46b62f63ad..4bd2ad5cb91b9 100644 --- a/integrations/terraform-mwi/go.sum +++ b/integrations/terraform-mwi/go.sum @@ -637,6 +637,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI= +code.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8= connectrpc.com/connect v1.19.0 h1:LuqUbq01PqbtL0o7vn0WMRXzR2nNsiINe5zfcJ24pJM= connectrpc.com/connect v1.19.0/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= @@ -910,6 +912,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47 github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0 h1:EJsHUYgFBV7/N1YtL73lsfZODAOU+CnNSZfEAlqqQaA= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0/go.mod h1:AxKuXHc0zv2yYaeueUG7R3ONbcnQIuDj0bkdFmPVRzU= +github.com/aws/rolesanywhere-credential-helper v1.2.0 h1:eLqJvSznH8nJk48dwFc0raWOpbTGgBeNYH3Q8UQFVx4= +github.com/aws/rolesanywhere-credential-helper v1.2.0/go.mod h1:YRxmRrAaqbVVXPNH1gHT76nWaMGvpAziHAHw8UwKrpU= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= @@ -1003,6 +1007,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4 h1:NZwojZ3+Cb8qxEkNdtmIrjwergNXvkTjvnM+yecyQoQ= +github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= github.com/coreos/go-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= @@ -1052,6 +1058,10 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dmarkham/enumer v1.6.1 h1:aSc9awYtZL07TUueWs40QcHtxTvHTAwG0EqrNsK45w4= github.com/dmarkham/enumer v1.6.1/go.mod h1:yixql+kDDQRYqcuBM2n9Vlt7NoT9ixgXhaXry8vmRg8= +github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= +github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= @@ -2060,6 +2070,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spiffe/aws-spiffe-workload-helper v0.0.4 h1:P39B0/nXbSHm2WIBkHgKYclGzu2AZVjTA5keEa6CnOk= +github.com/spiffe/aws-spiffe-workload-helper v0.0.4/go.mod h1:off3o6L61qUpoyoHIBSC7W75EVzAwMIRr2Va6Oy9988= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -2128,6 +2140,8 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/transparency-dev/tessera v1.0.0-rc3 h1:v385KqMekDUKI3ZVJHCHE5MAz8LBrWsEKa6OzYLrz0k= github.com/transparency-dev/tessera v1.0.0-rc3/go.mod h1:aaLlvG/sEPMzT96iIF4hua6Z9pLzkfDtkbaUAR4IL8I= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index bcb96a57d509c..2d63ee25e09a6 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -638,6 +638,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI= +code.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8= connectrpc.com/connect v1.19.0 h1:LuqUbq01PqbtL0o7vn0WMRXzR2nNsiINe5zfcJ24pJM= connectrpc.com/connect v1.19.0/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= @@ -930,6 +932,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47 github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0 h1:EJsHUYgFBV7/N1YtL73lsfZODAOU+CnNSZfEAlqqQaA= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0/go.mod h1:AxKuXHc0zv2yYaeueUG7R3ONbcnQIuDj0bkdFmPVRzU= +github.com/aws/rolesanywhere-credential-helper v1.2.0 h1:eLqJvSznH8nJk48dwFc0raWOpbTGgBeNYH3Q8UQFVx4= +github.com/aws/rolesanywhere-credential-helper v1.2.0/go.mod h1:YRxmRrAaqbVVXPNH1gHT76nWaMGvpAziHAHw8UwKrpU= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= @@ -1026,6 +1030,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4 h1:NZwojZ3+Cb8qxEkNdtmIrjwergNXvkTjvnM+yecyQoQ= +github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= github.com/coreos/go-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= @@ -1075,6 +1081,10 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dmarkham/enumer v1.6.1 h1:aSc9awYtZL07TUueWs40QcHtxTvHTAwG0EqrNsK45w4= github.com/dmarkham/enumer v1.6.1/go.mod h1:yixql+kDDQRYqcuBM2n9Vlt7NoT9ixgXhaXry8vmRg8= +github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= +github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= @@ -2142,6 +2152,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spiffe/aws-spiffe-workload-helper v0.0.4 h1:P39B0/nXbSHm2WIBkHgKYclGzu2AZVjTA5keEa6CnOk= +github.com/spiffe/aws-spiffe-workload-helper v0.0.4/go.mod h1:off3o6L61qUpoyoHIBSC7W75EVzAwMIRr2Va6Oy9988= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -2211,6 +2223,8 @@ github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXG github.com/transparency-dev/tessera v1.0.0-rc3 h1:v385KqMekDUKI3ZVJHCHE5MAz8LBrWsEKa6OzYLrz0k= github.com/transparency-dev/tessera v1.0.0-rc3/go.mod h1:aaLlvG/sEPMzT96iIF4hua6Z9pLzkfDtkbaUAR4IL8I= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/lib/autoupdate/agent/installer.go b/lib/autoupdate/agent/installer.go index e5e038fa4f68e..97239d69f74d5 100644 --- a/lib/autoupdate/agent/installer.go +++ b/lib/autoupdate/agent/installer.go @@ -56,8 +56,8 @@ const ( const ( // serviceDir contains the relative path to the Teleport SystemD service dir. serviceDir = "lib/systemd/system" - // serviceName contains the upstream name of the Teleport SystemD service file. - serviceName = "teleport.service" + // teleportServiceName contains the upstream name of the Teleport SystemD service file. + teleportServiceName = "teleport.service" ) // ServiceFile represents a systemd service file for a Teleport binary. @@ -380,7 +380,7 @@ func tgzExtractPaths(ent bool) []utils.ExtractPath { prefix += "-ent" } return []utils.ExtractPath{ - {Src: path.Join(prefix, "examples/systemd/teleport.service"), Dst: filepath.Join(serviceDir, serviceName), DirMode: systemDirMode}, + {Src: path.Join(prefix, "examples/systemd/teleport.service"), Dst: filepath.Join(serviceDir, teleportServiceName), DirMode: systemDirMode}, {Src: path.Join(prefix, "examples"), Skip: true, DirMode: systemDirMode}, {Src: path.Join(prefix, "install"), Skip: true, DirMode: systemDirMode}, {Src: path.Join(prefix, "install-selinux.sh"), Skip: true, DirMode: systemDirMode}, @@ -607,7 +607,7 @@ func (li *LocalInstaller) forceLinks(ctx context.Context, srcBinDir, srcSvcDir, return revert, trace.Wrap(ErrNoBinaries) } - // create systemd service files + // process systemd service files for _, s := range li.TargetServices { orig, err := copyService(s, srcSvcDir, dstBinDir, flags) @@ -853,6 +853,7 @@ func (li *LocalInstaller) tryLinks(ctx context.Context, srcBinDir, srcSvcDir, ds } } + // process systemd service files for _, s := range li.TargetServices { _, err := copyService(s, srcSvcDir, dstBinDir, flags) if err != nil && !errors.Is(err, os.ErrExist) { diff --git a/lib/autoupdate/agent/installer_test.go b/lib/autoupdate/agent/installer_test.go index 52fb7b4c89dd1..956146b61f9c3 100644 --- a/lib/autoupdate/agent/installer_test.go +++ b/lib/autoupdate/agent/installer_test.go @@ -214,7 +214,7 @@ func testTGZ(t *testing.T, version string) (tgz *bytes.Buffer, shasum string) { func TestLocalInstaller_Link(t *testing.T) { t.Parallel() const version = "new-version" - servicePath := filepath.Join(serviceDir, serviceName) + servicePath := filepath.Join(serviceDir, teleportServiceName) tests := []struct { name string @@ -459,8 +459,8 @@ func TestLocalInstaller_Link(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, serviceName), - ExampleName: serviceName, + Path: filepath.Join(linkDir, serviceDir, teleportServiceName), + ExampleName: teleportServiceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -527,7 +527,7 @@ func TestLocalInstaller_Link(t *testing.T) { func TestLocalInstaller_TryLink(t *testing.T) { t.Parallel() const version = "new-version" - servicePath := filepath.Join(serviceDir, serviceName) + servicePath := filepath.Join(serviceDir, teleportServiceName) tests := []struct { name string @@ -719,8 +719,8 @@ func TestLocalInstaller_TryLink(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, serviceName), - ExampleName: serviceName, + Path: filepath.Join(linkDir, serviceDir, teleportServiceName), + ExampleName: teleportServiceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -862,8 +862,8 @@ func TestLocalInstaller_Remove(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, serviceName), - ExampleName: serviceName, + Path: filepath.Join(linkDir, serviceDir, teleportServiceName), + ExampleName: teleportServiceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -889,7 +889,7 @@ func TestLocalInstaller_Remove(t *testing.T) { func TestLocalInstaller_IsLinked(t *testing.T) { t.Parallel() const version = "existing-version" - servicePath := filepath.Join(serviceDir, serviceName) + servicePath := filepath.Join(serviceDir, teleportServiceName) tests := []struct { name string @@ -937,8 +937,8 @@ func TestLocalInstaller_IsLinked(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, serviceName), - ExampleName: serviceName, + Path: filepath.Join(linkDir, serviceDir, teleportServiceName), + ExampleName: teleportServiceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) @@ -982,7 +982,7 @@ func TestLocalInstaller_IsLinked(t *testing.T) { func TestLocalInstaller_Unlink(t *testing.T) { t.Parallel() const version = "existing-version" - servicePath := filepath.Join(serviceDir, serviceName) + servicePath := filepath.Join(serviceDir, teleportServiceName) tests := []struct { name string @@ -1103,7 +1103,7 @@ func TestLocalInstaller_Unlink(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, serviceName), + Path: filepath.Join(linkDir, serviceDir, teleportServiceName), Binary: "teleport", }, }, diff --git a/lib/autoupdate/agent/internal/unversioned.go b/lib/autoupdate/agent/internal/unversioned.go new file mode 100644 index 0000000000000..4cb002861acc8 --- /dev/null +++ b/lib/autoupdate/agent/internal/unversioned.go @@ -0,0 +1,33 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package internal + +// UnversionedTeleport is used to read all versions of teleport.yaml, including +// versions that may now be unsupported. +type UnversionedTeleport struct { + Teleport UnversionedConfig `yaml:"teleport"` +} + +// UnversionedConfig is used to read unversioned configuration from teleport and tbot. +type UnversionedConfig struct { + AuthServers []string `yaml:"auth_servers"` + AuthServer string `yaml:"auth_server"` + ProxyServer string `yaml:"proxy_server"` + DataDir string `yaml:"data_dir"` +} diff --git a/lib/autoupdate/agent/internal/unversioned_test.go b/lib/autoupdate/agent/internal/unversioned_test.go new file mode 100644 index 0000000000000..696de1040f3c4 --- /dev/null +++ b/lib/autoupdate/agent/internal/unversioned_test.go @@ -0,0 +1,190 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package internal_test + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + + "github.com/gravitational/teleport/lib/autoupdate/agent/internal" + "github.com/gravitational/teleport/lib/config" + tbotconfig "github.com/gravitational/teleport/lib/tbot/config" +) + +// In the future, the latest version of the updater may need to read a version of teleport.yaml that has +// an unsupported version which is supported by the updater-managed version of Teleport. +// This test will break if Teleport removes a field that the updater reads. +func TestUnversionedTeleportConfig(t *testing.T) { + for _, tt := range []struct { + name string + version string + in internal.UnversionedTeleport + err bool + }{ + { + name: "empty", + in: internal.UnversionedTeleport{ + Teleport: internal.UnversionedConfig{ + ProxyServer: "proxy.example.com", + AuthServer: "auth.example.com", + AuthServers: []string{"auth1.example.com", "auth2.example.com"}, + DataDir: "example_dir", + }, + }, + }, + { + name: "v1", + version: string(tbotconfig.V1), + in: internal.UnversionedTeleport{ + Teleport: internal.UnversionedConfig{ + ProxyServer: "proxy.example.com", + AuthServer: "auth.example.com", + AuthServers: []string{"auth1.example.com", "auth2.example.com"}, + DataDir: "example_dir", + }, + }, + }, + { + name: "v2", + version: string(tbotconfig.V2), + in: internal.UnversionedTeleport{ + Teleport: internal.UnversionedConfig{ + ProxyServer: "proxy.example.com", + AuthServer: "auth.example.com", + AuthServers: []string{"auth1.example.com", "auth2.example.com"}, + DataDir: "example_dir", + }, + }, + }, + { + name: "v3", // if this fails, add any new fields to the unversioned config + version: "v3", + in: internal.UnversionedTeleport{ + Teleport: internal.UnversionedConfig{ + ProxyServer: "proxy.example.com", + AuthServer: "auth.example.com", + AuthServers: []string{"auth1.example.com", "auth2.example.com"}, + DataDir: "example_dir", + }, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + in := struct { + Version string `yaml:"version"` + internal.UnversionedTeleport `yaml:",inline"` + }{ + Version: tt.version, + UnversionedTeleport: tt.in, + } + var inB bytes.Buffer + err := yaml.NewEncoder(&inB).Encode(in) + require.NoError(t, err) + fc, err := config.ReadConfig(&inB) + if tt.err { + require.Error(t, err) + return + } + require.NoError(t, err) + + var outB bytes.Buffer + err = yaml.NewEncoder(&outB).Encode(fc) + require.NoError(t, err) + + var out internal.UnversionedTeleport + err = yaml.NewDecoder(&outB).Decode(&out) + require.NoError(t, err) + require.Equal(t, tt.in, out) + }) + } + +} + +// In the future, the latest version of the updater may need to read a version of tbot.yaml that has +// an unsupported version which is supported by the updater-managed version of tbot. +// This test will break if tbot removes a field that the updater reads. +func TestUnversionedTbotConfig(t *testing.T) { + for _, tt := range []struct { + name string + version string + in internal.UnversionedConfig + err bool + }{ + { + name: "empty", + in: internal.UnversionedConfig{ + AuthServer: "auth.example.com", + }, + err: true, + }, + { + name: "v1", + version: string(tbotconfig.V1), + in: internal.UnversionedConfig{ + AuthServer: "auth.example.com", + }, + err: true, + }, + { + name: "v2", + version: string(tbotconfig.V2), + in: internal.UnversionedConfig{ + AuthServer: "auth.example.com", + ProxyServer: "proxy.example.com", + }, + }, + { + name: "v3", // when this fails, add any new fields to the unversioned config + version: "v3", + in: internal.UnversionedConfig{}, + err: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + in := struct { + Version string `yaml:"version"` + internal.UnversionedConfig `yaml:",inline"` + }{ + Version: tt.version, + UnversionedConfig: tt.in, + } + var inB bytes.Buffer + err := yaml.NewEncoder(&inB).Encode(in) + require.NoError(t, err) + fc, err := tbotconfig.ReadConfig(bytes.NewReader(inB.Bytes()), false) + if tt.err { + require.Error(t, err) + return + } + require.NoError(t, err) + + var outB bytes.Buffer + err = yaml.NewEncoder(&outB).Encode(fc) + require.NoError(t, err) + + var out internal.UnversionedConfig + err = yaml.NewDecoder(&outB).Decode(&out) + require.NoError(t, err) + require.Equal(t, tt.in, out) + }) + } +} diff --git a/lib/autoupdate/agent/process.go b/lib/autoupdate/agent/process.go index cd76c04cd3e4f..e537a4040b688 100644 --- a/lib/autoupdate/agent/process.go +++ b/lib/autoupdate/agent/process.go @@ -66,6 +66,8 @@ type SystemdService struct { Ready ReadyChecker // Log contains a logger. Log *slog.Logger + // ForceRestart forces the process to always restart. + ForceRestart bool } // ReadyChecker returns the systemd service readiness status. @@ -73,6 +75,11 @@ type ReadyChecker interface { GetReadiness(ctx context.Context) (debug.Readiness, error) } +// Name of the systemd service. +func (s SystemdService) Name() string { + return s.ServiceName +} + // Reload the systemd service. // Attempts a graceful reload before a hard restart. // See Process interface for more details. @@ -86,7 +93,7 @@ func (s SystemdService) Reload(ctx context.Context) error { // Command error codes < 0 indicate that we are unable to run the command. // Errors from s.systemctl are logged along with stderr and stdout (debug only). - // If the service is not running, return ErrNotNeeded. + // If the service is not running, return nil. // Note systemctl reload returns an error if the unit is not active, and // try-reload-or-restart is too recent of an addition for centos7. code := s.systemctl(ctx, slog.LevelDebug, "is-active", "--quiet", s.ServiceName) @@ -95,7 +102,7 @@ func (s SystemdService) Reload(ctx context.Context) error { return trace.Errorf("unable to determine if systemd service is active") case code > 0: s.Log.WarnContext(ctx, "Systemd service not running.", unitKey, s.ServiceName) - return trace.Wrap(ErrNotNeeded) + return nil } // Get initial PID for crash monitoring. @@ -108,20 +115,28 @@ func (s SystemdService) Reload(ctx context.Context) error { } // Attempt graceful reload of running service. - code = s.systemctl(ctx, slog.LevelError, "reload", s.ServiceName) - switch { - case code < 0: - return trace.Errorf("unable to reload systemd service") - case code > 0: - // Graceful reload fails, try hard restart. + if !s.ForceRestart { + code = s.systemctl(ctx, slog.LevelError, "reload", s.ServiceName) + switch { + case code < 0: + return trace.Errorf("unable to reload systemd service") + case code > 0: + // Graceful reload fails, try hard restart. + code = s.systemctl(ctx, slog.LevelError, "try-restart", s.ServiceName) + if code != 0 { + return trace.Errorf("hard restart of systemd service failed") + } + s.Log.WarnContext(ctx, "Service ungracefully restarted. Connections potentially dropped.", unitKey, s.ServiceName) + default: + s.Log.InfoContext(ctx, "Gracefully reloaded.", unitKey, s.ServiceName) + } + } else { code = s.systemctl(ctx, slog.LevelError, "try-restart", s.ServiceName) if code != 0 { return trace.Errorf("hard restart of systemd service failed") } - s.Log.WarnContext(ctx, "Service ungracefully restarted. Connections potentially dropped.", unitKey, s.ServiceName) - default: - s.Log.InfoContext(ctx, "Gracefully reloaded.", unitKey, s.ServiceName) } + // monitor logs all relevant errors, so we filter for a few outcomes err = s.monitor(ctx, initPID) if errors.Is(err, context.DeadlineExceeded) || @@ -301,6 +316,9 @@ func tickFile(ctx context.Context, path string, ch chan<- int, tickC <-chan time // waitForReady polls the SocketPath unix domain socket with HTTP requests. // If one request returns 200 before the timeout, the service is considered ready. func (s SystemdService) waitForReady(ctx context.Context, pid int, tickC <-chan time.Time) error { + if s.Ready == nil { + return nil + } var lastErr error var readiness debug.Readiness for { @@ -531,6 +549,67 @@ func (s SystemdService) systemctl(ctx context.Context, errLevel slog.Level, args return code } +// ProcessGroup is a group of other Teleport processes. +type ProcessGroup []Process + +func (p ProcessGroup) Name() string { + return "Teleport services" +} + +// Reload reloads all processes in the process group. +func (p ProcessGroup) Reload(ctx context.Context) error { + // TODO(sclevine): consider reloading in parallel if this is too slow for users + for _, process := range p { + if err := process.Reload(ctx); err != nil { + return trace.Wrap(err, "failed to reload %s", process.Name()) + } + } + return nil +} + +// Sync syncs only the first process in the group, and fails if no processes are present. +// The systemctl daemon-reload command is global, so we only need to sync once. +func (p ProcessGroup) Sync(ctx context.Context) error { + if len(p) == 0 { + return trace.Errorf("no services to sync") + } + return trace.Wrap(p[0].Sync(ctx)) +} + +// IsEnabled returns true if any processes in the group are enabled. +func (p ProcessGroup) IsEnabled(ctx context.Context) (bool, error) { + return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { + return p.IsEnabled(ctx) + }) +} + +// IsPresent returns true if any processes in the group are present. +func (p ProcessGroup) IsPresent(ctx context.Context) (bool, error) { + return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { + return p.IsPresent(ctx) + }) +} + +// IsActive returns true if any processes in the group are active. +func (p ProcessGroup) IsActive(ctx context.Context) (bool, error) { + return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { + return p.IsActive(ctx) + }) +} + +func (p ProcessGroup) anyAreTrue(ctx context.Context, f func(ctx context.Context, p Process) (bool, error)) (bool, error) { + for _, process := range p { + ok, err := f(ctx, process) + if err != nil { + return ok, trace.Wrap(err) + } + if ok { + return true, nil + } + } + return false, nil +} + // localExec runs a command locally, logging any output. type localExec struct { // Dir specifies the working directory of the local command. diff --git a/lib/autoupdate/agent/setup.go b/lib/autoupdate/agent/setup.go index 8dfb340c46c96..ef2accb9ff4ad 100644 --- a/lib/autoupdate/agent/setup.go +++ b/lib/autoupdate/agent/setup.go @@ -38,9 +38,11 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/autoupdate" + "github.com/gravitational/teleport/lib/autoupdate/agent/internal" "github.com/gravitational/teleport/lib/config/systemd" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/selinux" + tbotsystemd "github.com/gravitational/teleport/lib/tbot/config/systemd" libutils "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/versioncontrol" ) @@ -65,18 +67,20 @@ const ( deprecatedServiceName = "teleport-upgrade.service" ) -// genHeader generates a systemd config file header that starts -// with the serviceMarker. -func genHeader(rev Revision) string { - return genMarker(rev) + - "# DO NOT EDIT THIS FILE\n" -} +const markerPrefix = "# teleport-update " // genMarker generates a systemd config file marker that is the // first part of the header for systemd service files. // Each revision of Teleport has a unique marker. func genMarker(rev Revision) string { - return "# teleport-update " + rev.Dir() + "\n" + return markerPrefix + rev.Dir() + "\n" +} + +// genHeader generates a systemd config file header that starts +// with the serviceMarker. +func genHeader(rev Revision) string { + return genMarker(rev) + + "# DO NOT EDIT THIS FILE\n" } const ( @@ -111,22 +115,21 @@ Environment="TELEPORT_UPDATE_INSTALL_DIR={{escape .InstallDir}}" ExecStart= ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." ` + // This configuration sets the default value for needrestart-trigger automatic restarts for teleport.service to disabled. // Users may still choose to enable needrestart for teleport.service when installing packaging interactively (or via dpkg config), // but doing so will result in a hard restart that disconnects the agent whenever any dependent libraries are updated. // Other network services, like openvpn, follow this pattern. // It is possible to configure needrestart to trigger a soft restart (via restart.d script), but given that Teleport subprocesses // can use a wide variety of installed binaries (when executed by the user), this could trigger many unexpected reloads. - needrestartConfTemplate = `$nrconf{override_rc}{qr(^{{replace .TeleportService "." "\\."}})} = 0; -` + needrestartConfTemplate = `{{range .Services}}$nrconf{override_rc}{qr(^{{replace . "." "\\."}})} = 0; +{{end}}` ) -// standard Makefile included in 'selinux-policy-devel' RPM package -// used to build SELinux modules -const selinuxMakefile = "/usr/share/selinux/devel/Makefile" - +// confParams parameterizes the above updater configuration templates. type confParams struct { TeleportService string + Services []string UpdaterBinary string InstallSuffix string InstallDir string @@ -134,6 +137,10 @@ type confParams struct { UpdaterConfigFile string } +// standard Makefile included in 'selinux-policy-devel' RPM package +// used to build SELinux modules +const selinuxMakefile = "/usr/share/selinux/devel/Makefile" + // Namespace represents a namespace within various system paths for a isolated installation of Teleport. type Namespace struct { log *slog.Logger @@ -143,28 +150,34 @@ type Namespace struct { installDir string // defaultPathDir for Teleport binaries (ns: /opt/teleport/myns/bin) defaultPathDir string - // dataDir parsed from teleport.yaml, if present - dataDir string // defaultProxyAddr parsed from teleport.yaml, if present defaultProxyAddr string - // serviceFile for the Teleport systemd service (ns: /etc/systemd/system/teleport_myns.service) - serviceFile string - // configFile for Teleport config (ns: /etc/teleport_myns.yaml) - configFile string - // pidFile for Teleport (ns: /run/teleport_myns.pid) - pidFile string + // dataDir parsed from teleport.yaml, if present + dataDir string + // teleportServiceFile for the Teleport systemd service (ns: /etc/systemd/system/teleport_myns.service) + teleportServiceFile string + // teleportConfigFile for Teleport config (ns: /etc/teleport_myns.yaml) + teleportConfigFile string + // teleportPIDFile for Teleport (ns: /run/teleport_myns.pid) + teleportPIDFile string + // teleportDropInFile is the Teleport systemd drop-in path extending Teleport + teleportDropInFile string + // tbotServiceFile is the systemd service path for tbot (ns: /etc/systemd/system/tbot_myns.service) + tbotServiceFile string + // tbotConfigFile for tbot config (ns: /etc/tbot_myns.yaml) + tbotConfigFile string + // tbotPIDFile for tbot (ns: /run/tbot_myns.pid) + tbotPIDFile string // updaterIDFile contains the updater's temporary ID file updaterIDFile string // updaterServiceFile is the systemd service path for the updater updaterServiceFile string // updaterTimerFile is the systemd timer path for the updater updaterTimerFile string - // teleportDropInFile is the Teleport systemd drop-in path extending Teleport - teleportDropInFile string + // needrestartConfigFile is the path to needrestart configuration for all services + needrestartConfigFile string // deprecatedDropInFile is the deprecated upgrader's systemd drop-in path deprecatedDropInFile string - // needrestartConfFile is the path to needrestart configuration for Teleport - needrestartConfFile string } var alphanum = regexp.MustCompile("^[a-zA-Z0-9-]*$") @@ -188,39 +201,45 @@ func NewNamespace(ctx context.Context, log *slog.Logger, name, installDir string if name == "" { linkDir := defaultPathDir return &Namespace{ - log: log, - name: name, - installDir: installDir, - defaultPathDir: linkDir, - dataDir: defaults.DataDir, - serviceFile: filepath.Join("/", serviceDir, serviceName), - configFile: defaults.ConfigFilePath, - pidFile: filepath.Join(systemdPIDDir, "teleport.pid"), - updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), - updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), - updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), - teleportDropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), - deprecatedDropInFile: filepath.Join(systemdAdminDir, deprecatedServiceName+".d", BinaryName+".conf"), - needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), + log: log, + name: name, + installDir: installDir, + defaultPathDir: linkDir, + dataDir: defaults.DataDir, + teleportServiceFile: filepath.Join("/", serviceDir, teleportServiceName), // /lib for backwards-compat + teleportConfigFile: defaults.ConfigFilePath, + teleportPIDFile: filepath.Join(systemdPIDDir, "teleport.pid"), + needrestartConfigFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), + teleportDropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), + updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), + updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), + updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), + deprecatedDropInFile: filepath.Join(systemdAdminDir, deprecatedServiceName+".d", BinaryName+".conf"), + tbotServiceFile: filepath.Join(systemdAdminDir, "tbot.service"), + tbotConfigFile: filepath.Join("/etc", "tbot.yaml"), + tbotPIDFile: filepath.Join(systemdPIDDir, "tbot.pid"), }, nil } prefix := "teleport_" + name linkDir := filepath.Join(installDir, name, "bin") return &Namespace{ - log: log, - name: name, - installDir: installDir, - defaultPathDir: linkDir, - dataDir: filepath.Join(filepath.Dir(defaults.DataDir), prefix), - serviceFile: filepath.Join(systemdAdminDir, prefix+".service"), - configFile: filepath.Join(filepath.Dir(defaults.ConfigFilePath), prefix+".yaml"), - pidFile: filepath.Join(systemdPIDDir, prefix+".pid"), - updaterIDFile: filepath.Join(os.TempDir(), BinaryName+"_"+name+".id"), - updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".service"), - updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".timer"), - teleportDropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), - needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+"_"+name+".conf"), + log: log, + name: name, + installDir: installDir, + defaultPathDir: linkDir, + dataDir: filepath.Join(filepath.Dir(defaults.DataDir), prefix), + teleportServiceFile: filepath.Join(systemdAdminDir, prefix+".service"), + teleportConfigFile: filepath.Join(filepath.Dir(defaults.ConfigFilePath), prefix+".yaml"), + teleportPIDFile: filepath.Join(systemdPIDDir, prefix+".pid"), + needrestartConfigFile: filepath.Join(needrestartConfDir, BinaryName+"_"+name+".conf"), + teleportDropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), + updaterIDFile: filepath.Join(os.TempDir(), BinaryName+"_"+name+".id"), + updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".service"), + updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".timer"), + tbotServiceFile: filepath.Join(systemdAdminDir, "tbot_"+name+".service"), + tbotConfigFile: filepath.Join("/etc", "tbot_"+name+".yaml"), + tbotPIDFile: filepath.Join(systemdPIDDir, "tbot_"+name+".pid"), // no deprecatedDropInFile, as teleport-upgrade does not conflict with namespaced installs }, nil } @@ -315,7 +334,7 @@ func (ns *Namespace) installSELinux(ctx context.Context, rev Revision) error { } binaryPath := filepath.Join(ns.Dir(), versionsDirName, rev.Dir(), "bin", "teleport") - fileCtxs, err := selinux.FileContexts(ns.dataDir, ns.configFile, binaryPath) + fileCtxs, err := selinux.FileContexts(ns.dataDir, ns.teleportConfigFile, binaryPath) if err != nil { return trace.Wrap(err) } @@ -378,17 +397,17 @@ func (ns *Namespace) createAndLabelDirs(ctx context.Context, cmd localExec) erro } // Create an empty teleport.yaml config file if it doesn't exist, and // only attempt to label it if it exists. - if libutils.FileExists(ns.configFile) { - dirsToLabel = append(dirsToLabel, filepath.Dir(ns.configFile)) + if libutils.FileExists(ns.teleportConfigFile) { + dirsToLabel = append(dirsToLabel, filepath.Dir(ns.teleportConfigFile)) } else { - confFile, err := os.OpenFile(ns.configFile, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0o640) + confFile, err := os.OpenFile(ns.teleportConfigFile, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0o640) if err != nil { - ns.log.WarnContext(ctx, "Failed to create teleport.yaml.", errorKey, err, "path", ns.configFile) + ns.log.WarnContext(ctx, "Failed to create teleport.yaml.", errorKey, err, "path", ns.teleportConfigFile) ns.log.WarnContext(ctx, "You will likely need to create teleport.yaml and re-run 'sudo teleport-update enable' for Teleport SSH to work correctly with SELinux.") } else { confFile.Close() - dirsToLabel = append(dirsToLabel, filepath.Dir(ns.configFile)) - ns.log.WarnContext(ctx, "Created an empty teleport.yaml.", "path", ns.configFile) + dirsToLabel = append(dirsToLabel, filepath.Dir(ns.teleportConfigFile)) + ns.log.WarnContext(ctx, "Created an empty teleport.yaml.", "path", ns.teleportConfigFile) ns.log.WarnContext(ctx, "If you move or copy another file onto this file you will likely need to re-run 'sudo teleport-update enable' for Teleport SSH to work correctly with SELinux.") } } @@ -433,7 +452,7 @@ func (ns *Namespace) removeSELinux(ctx context.Context) error { return trace.Wrap(err, "failed to remove module") } - _, err = cmd.Run(ctx, "restorecon", "-rv", filepath.Clean(ns.installDir), ns.dataDir, filepath.Dir(ns.configFile)) + _, err = cmd.Run(ctx, "restorecon", "-rv", filepath.Clean(ns.installDir), ns.dataDir, filepath.Dir(ns.teleportConfigFile)) if err != nil { return trace.Wrap(err, "failed to restore file contexts") } @@ -504,7 +523,7 @@ func (ns *Namespace) Teardown(ctx context.Context) error { ns.updaterTimerFile, ns.teleportDropInFile, ns.deprecatedDropInFile, - ns.needrestartConfFile, + ns.needrestartConfigFile, } { if p == "" { continue @@ -550,7 +569,8 @@ func (ns *Namespace) Teardown(ctx context.Context) error { } func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revision) error { - teleportService := filepath.Base(ns.serviceFile) + teleportService := filepath.Base(ns.teleportServiceFile) + tbotService := filepath.Base(ns.tbotServiceFile) params := confParams{ TeleportService: teleportService, UpdaterBinary: filepath.Join(path, BinaryName), @@ -576,8 +596,17 @@ func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revi return trace.Wrap(err) } } + // Needrestart config is non-critical for updater functionality. - _, err := os.Stat(filepath.Dir(ns.needrestartConfFile)) + + nrServices := []string{teleportService} + if ok, err := ns.HasCustomTbot(ctx); err != nil { + ns.log.ErrorContext(ctx, "Unable to determine if tbot is managed by the updater, skipping needrestart configuration.", errorKey, err) + } else if !ok { + nrServices = append(nrServices, tbotService) + } + + _, err := os.Stat(filepath.Dir(ns.needrestartConfigFile)) if os.IsNotExist(err) { return nil // needrestart is not present } @@ -585,8 +614,10 @@ func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revi ns.log.ErrorContext(ctx, "Unable to disable needrestart.", errorKey, err) return nil } - ns.log.InfoContext(ctx, "Disabling needrestart.", unitKey, teleportService) - err = writeSystemTemplate(ns.needrestartConfFile, "", needrestartConfTemplate, params) + ns.log.InfoContext(ctx, "Disabling needrestart.") + err = writeSystemTemplate(ns.needrestartConfigFile, "", needrestartConfTemplate, confParams{ + Services: nrServices, + }) if err != nil { ns.log.ErrorContext(ctx, "Unable to disable needrestart.", errorKey, err) return nil @@ -638,22 +669,72 @@ func (ns *Namespace) WriteTeleportService(_ context.Context, pathDir string, rev if pathDir == "" { pathDir = ns.defaultPathDir } - return trace.Wrap(writeAtomicWithinDir(ns.serviceFile, configFileMode, func(w io.Writer) error { + return trace.Wrap(writeAtomicWithinDir(ns.teleportServiceFile, configFileMode, func(w io.Writer) error { _, err := fmt.Fprint(w, genHeader(rev)+"\n") if err != nil { return trace.Wrap(err) } return trace.Wrap(systemd.WriteUnitFile(systemd.Flags{ EnvironmentFile: systemd.DefaultEnvironmentFile, - PIDFile: ns.pidFile, + PIDFile: ns.teleportPIDFile, FileDescriptorLimit: systemd.DefaultFileDescriptorLimit, TeleportInstallationFile: filepath.Join(pathDir, "teleport"), - TeleportConfigPath: ns.configFile, + TeleportConfigPath: ns.teleportConfigFile, FIPS: rev.Flags&autoupdate.FlagFIPS != 0, }, w)) })) } +func (ns *Namespace) HasCustomTbot(ctx context.Context) (bool, error) { + diskMarkerPrefix, err := readFileLimit(ns.tbotServiceFile, int64(len(markerPrefix))) + if errors.Is(err, os.ErrNotExist) { + ns.log.DebugContext(ctx, "Service not present.", "path", ns.tbotServiceFile) + return false, nil + } + if err != nil { + return false, trace.Wrap(err) + } + return string(diskMarkerPrefix) != markerPrefix, nil +} + +// WriteTbotService writes the tbot systemd service for the version of tbot +// that matches the version of tbot compiled into the executing code. +func (ns *Namespace) WriteTbotService(ctx context.Context, pathDir string, rev Revision) error { + if pathDir == "" { + pathDir = ns.defaultPathDir + } + // This configuration preserves the original tbot systemd.tmpl params. + // In the future, the template could be updated to accept PIDFile directly. + unitName := "tbot" + if ns.name != "" { + unitName = "tbot_" + ns.name + } + if ns.tbotPIDFile != filepath.Join("/run", unitName+".pid") { + return trace.Errorf("invalid PID file: %q", ns.tbotPIDFile) + } + diagDir := filepath.Join(ns.dataDir, "bot") + + if err := os.MkdirAll(diagDir, teleport.PrivateDirMode); err != nil { + ns.log.WarnContext(ctx, "Failed to create tbot data directory.", errorKey, err) + } + + return trace.Wrap(writeAtomicWithinDir(ns.tbotServiceFile, configFileMode, func(w io.Writer) error { + _, err := fmt.Fprint(w, genHeader(rev)+"\n") + if err != nil { + return trace.Wrap(err) + } + return trace.Wrap(tbotsystemd.Template.Execute(w, tbotsystemd.TemplateParams{ + UnitName: unitName, + User: "root", + Group: "root", + AnonymousTelemetry: true, + ConfigPath: ns.tbotConfigFile, + TBotPath: filepath.Join(pathDir, "tbot"), + DiagSocketForUpdater: filepath.Join(diagDir, teleport.DebugServiceSocketName), + })) + })) +} + // ReplaceTeleportService replaces the default paths in the Teleport service config with namespaced paths. // This function is still used for backwards-compatibility, but string-replaced systemd services // are always overridden in more recent versions of Teleport. @@ -674,11 +755,11 @@ func (ns *Namespace) ReplaceTeleportService(cfg []byte, path string, flags autou }, { old: "/etc/teleport.yaml", - new: ns.configFile, + new: ns.teleportConfigFile, }, { old: "/run/teleport.pid", - new: ns.pidFile, + new: ns.teleportPIDFile, }, { old: "/teleport start ", @@ -705,50 +786,76 @@ func (ns *Namespace) LogWarnings(ctx context.Context, pathDir string) { if pathDir == "" { pathDir = ns.defaultPathDir } - ns.log.WarnContext(ctx, "Custom install suffix specified. Teleport data_dir must be configured in the config file.", + ns.log.WarnContext(ctx, "Custom install suffix specified.", "suffix", ns.name, "path_dir", pathDir) + ns.log.WarnContext(ctx, "Teleport data_dir must be set in the config file, and paths will be different.", "data_dir", ns.dataDir, - "path_dir", pathDir, - "config_file", ns.configFile, - "service", filepath.Base(ns.serviceFile), - "pid_file", ns.pidFile, + "config_file", ns.teleportConfigFile, + "service", filepath.Base(ns.teleportServiceFile), + "pid_file", ns.teleportPIDFile, + ) + ns.log.WarnContext(ctx, "Tbot data storage must be set in the config file, and paths will be different.", + "data_dir", filepath.Join(ns.dataDir, "bot"), + "config_file", ns.tbotConfigFile, + "service", filepath.Base(ns.tbotServiceFile), + "pid_file", ns.tbotPIDFile, ) } -// unversionedConfig is used to read all versions of teleport.yaml, including -// versions that may now be unsupported. -type unversionedConfig struct { - Teleport unversionedTeleport `yaml:"teleport"` -} - -type unversionedTeleport struct { - AuthServers []string `yaml:"auth_servers"` - AuthServer string `yaml:"auth_server"` - ProxyServer string `yaml:"proxy_server"` - DataDir string `yaml:"data_dir"` -} - -// overrideFromConfig loads fields from teleport.yaml into the namespace, overriding any defaults. +// overrideFromConfig loads fields from teleport configuration into the namespace, overriding any defaults. func (ns *Namespace) overrideFromConfig(ctx context.Context) { - if ns == nil || ns.configFile == "" { - return - } - path := ns.configFile - f, err := libutils.OpenFileAllowingUnsafeLinks(path) - if err != nil { - ns.log.DebugContext(ctx, "Unable to open Teleport config to read proxy or data dir", "config", path, errorKey, err) + if ns == nil { return } - defer f.Close() - var cfg unversionedConfig - if err := yaml.NewDecoder(f).Decode(&cfg); err != nil { - ns.log.DebugContext(ctx, "Unable to parse Teleport config to read proxy or data dir", "config", path, errorKey, err) + var ( + configFile string + cfg internal.UnversionedTeleport + ) + + switch { + case ns.teleportConfigFile != "": + // Use values from teleport.yaml if present. + configFile = ns.teleportConfigFile + f, err := libutils.OpenFileAllowingUnsafeLinks(configFile) + if err != nil { + ns.log.DebugContext(ctx, "Unable to open Teleport config to read proxy or data dir", "config_file", configFile, errorKey, err) + return + } + defer f.Close() + if err := yaml.NewDecoder(f).Decode(&cfg); err != nil { + ns.log.DebugContext(ctx, "Unable to parse Teleport config to read proxy or data dir", "config_file", configFile, errorKey, err) + return + } + if cfg.Teleport.DataDir != "" { + ns.dataDir = cfg.Teleport.DataDir + } + case ns.tbotConfigFile != "": + // Otherwise, use values from tbot.yaml (bot-only installation). + // Do not attempt to read tbot's storage dir as the data directory used to store the tbot socket. + // Ignore tbot configuration if the updater does not manage the tbot service. + configFile = ns.tbotConfigFile + customTbot, err := ns.HasCustomTbot(ctx) + if err != nil { + ns.log.DebugContext(ctx, "Unable to determine if tbot is managed by the updater", "config_file", configFile, errorKey, err) + return + } + if customTbot { + return + } + f, err := libutils.OpenFileAllowingUnsafeLinks(configFile) + if err != nil { + ns.log.DebugContext(ctx, "Unable to open tbot config to read proxy or data dir", "config_file", configFile, errorKey, err) + return + } + defer f.Close() + if err := yaml.NewDecoder(f).Decode(&cfg.Teleport); err != nil { + ns.log.DebugContext(ctx, "Unable to parse tbot config to read proxy or data dir", "config_file", configFile, errorKey, err) + return + } + default: return } - if cfg.Teleport.DataDir != "" { - ns.dataDir = cfg.Teleport.DataDir - } - // Any implicitly defaulted port in teleport.yaml is explicitly defaulted (to 3080). + // Any implicitly defaulted port in configuration is explicitly defaulted (to 3080). var addr string var port int @@ -763,12 +870,12 @@ func (ns *Namespace) overrideFromConfig(ctx context.Context) { addr = t.AuthServers[0] port = defaults.AuthListenPort default: - ns.log.DebugContext(ctx, "Unable to find proxy in Teleport config", "config", path, errorKey, err) + ns.log.DebugContext(ctx, "Unable to find proxy in config", "config_file", configFile) return } netaddr, err := libutils.ParseHostPortAddr(addr, port) if err != nil { - ns.log.DebugContext(ctx, "Unable to parse proxy in Teleport config", "config", path, "proxy_addr", addr, "proxy_port", port, errorKey, err) + ns.log.DebugContext(ctx, "Unable to parse proxy in config", "config_file", configFile, "proxy_addr", addr, "proxy_port", port, errorKey, err) return } ns.defaultProxyAddr = netaddr.String() diff --git a/lib/autoupdate/agent/setup_test.go b/lib/autoupdate/agent/setup_test.go index 42311e56fefbd..dc24787db3d23 100644 --- a/lib/autoupdate/agent/setup_test.go +++ b/lib/autoupdate/agent/setup_test.go @@ -31,7 +31,7 @@ import ( "gopkg.in/yaml.v3" "github.com/gravitational/teleport/lib/autoupdate" - "github.com/gravitational/teleport/lib/config" + "github.com/gravitational/teleport/lib/autoupdate/agent/internal" "github.com/gravitational/teleport/lib/utils/testutils/golden" ) @@ -46,54 +46,63 @@ func TestNewNamespace(t *testing.T) { { name: "no namespace", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/opt/teleport", - defaultPathDir: "/usr/local/bin", - serviceFile: "/lib/systemd/system/teleport.service", - configFile: "/etc/teleport.yaml", - pidFile: "/run/teleport.pid", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", + dataDir: "/var/lib/teleport", + installDir: "/opt/teleport", + defaultPathDir: "/usr/local/bin", + teleportServiceFile: "/lib/systemd/system/teleport.service", + teleportConfigFile: "/etc/teleport.yaml", + teleportPIDFile: "/run/teleport.pid", + needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update.conf", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + tbotServiceFile: "/etc/systemd/system/tbot.service", + tbotConfigFile: "/etc/tbot.yaml", + tbotPIDFile: "/run/tbot.pid", }, }, { name: "no namespace with dirs", installDir: "/install", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/install", - defaultPathDir: "/usr/local/bin", - serviceFile: "/lib/systemd/system/teleport.service", - configFile: "/etc/teleport.yaml", - pidFile: "/run/teleport.pid", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", + dataDir: "/var/lib/teleport", + installDir: "/install", + defaultPathDir: "/usr/local/bin", + teleportServiceFile: "/lib/systemd/system/teleport.service", + teleportConfigFile: "/etc/teleport.yaml", + teleportPIDFile: "/run/teleport.pid", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + tbotServiceFile: "/etc/systemd/system/tbot.service", + tbotConfigFile: "/etc/tbot.yaml", + tbotPIDFile: "/run/tbot.pid", + needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update.conf", }, }, { name: "test namespace", namespace: "test", ns: &Namespace{ - name: "test", - dataDir: "/var/lib/teleport_test", - installDir: "/opt/teleport", - defaultPathDir: "/opt/teleport/test/bin", - serviceFile: "/etc/systemd/system/teleport_test.service", - configFile: "/etc/teleport_test.yaml", - pidFile: "/run/teleport_test.pid", - updaterIDFile: "/TMP/teleport-update_test.id", - updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", - updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", + name: "test", + dataDir: "/var/lib/teleport_test", + installDir: "/opt/teleport", + defaultPathDir: "/opt/teleport/test/bin", + teleportServiceFile: "/etc/systemd/system/teleport_test.service", + teleportConfigFile: "/etc/teleport_test.yaml", + teleportPIDFile: "/run/teleport_test.pid", + updaterIDFile: "/TMP/teleport-update_test.id", + updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", + updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + tbotServiceFile: "/etc/systemd/system/tbot_test.service", + tbotConfigFile: "/etc/tbot_test.yaml", + tbotPIDFile: "/run/tbot_test.pid", + needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, { @@ -101,18 +110,21 @@ func TestNewNamespace(t *testing.T) { namespace: "test", installDir: "/install", ns: &Namespace{ - name: "test", - dataDir: "/var/lib/teleport_test", - installDir: "/install", - defaultPathDir: "/install/test/bin", - configFile: "/etc/teleport_test.yaml", - pidFile: "/run/teleport_test.pid", - serviceFile: "/etc/systemd/system/teleport_test.service", - updaterIDFile: "/TMP/teleport-update_test.id", - updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", - updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", - needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", + name: "test", + dataDir: "/var/lib/teleport_test", + installDir: "/install", + defaultPathDir: "/install/test/bin", + teleportConfigFile: "/etc/teleport_test.yaml", + teleportPIDFile: "/run/teleport_test.pid", + teleportServiceFile: "/etc/systemd/system/teleport_test.service", + updaterIDFile: "/TMP/teleport-update_test.id", + updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", + updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + tbotServiceFile: "/etc/systemd/system/tbot_test.service", + tbotConfigFile: "/etc/tbot_test.yaml", + tbotPIDFile: "/run/tbot_test.pid", + needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, { @@ -147,8 +159,9 @@ func TestNewNamespace(t *testing.T) { func TestWriteConfigFiles(t *testing.T) { for _, p := range []struct { - name string - namespace string + name string + namespace string + customTbot bool }{ { name: "no namespace", @@ -157,6 +170,14 @@ func TestWriteConfigFiles(t *testing.T) { name: "test namespace", namespace: "test", }, + { + name: "test with custom tbot", + customTbot: true, + }, + { + name: "test namespace with custom tbot", + customTbot: true, + }, } { t.Run(p.name, func(t *testing.T) { log := slog.Default() @@ -164,12 +185,18 @@ func TestWriteConfigFiles(t *testing.T) { ctx := context.Background() ns, err := NewNamespace(ctx, log, p.namespace, "") require.NoError(t, err) - ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), filepath.Base(ns.updaterServiceFile)) ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterServiceFile) ns.updaterTimerFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterTimerFile) ns.teleportDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.teleportDropInFile))), ns.teleportDropInFile) ns.deprecatedDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.deprecatedDropInFile))), ns.deprecatedDropInFile) - ns.needrestartConfFile = rebasePath(linkDir, filepath.Base(ns.needrestartConfFile)) + ns.needrestartConfigFile = rebasePath(linkDir, filepath.Base(ns.needrestartConfigFile)) + if p.customTbot { + ns.tbotServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.tbotServiceFile) + err := os.MkdirAll(filepath.Dir(ns.tbotServiceFile), os.ModePerm) + require.NoError(t, err) + err = os.WriteFile(ns.tbotServiceFile, []byte("custom"), os.ModePerm) + require.NoError(t, err) + } err = ns.writeConfigFiles(ctx, linkDir, NewRevision("version", 0)) require.NoError(t, err) @@ -181,7 +208,7 @@ func TestWriteConfigFiles(t *testing.T) { {name: "timer", path: ns.updaterTimerFile}, {name: "dropin", path: ns.teleportDropInFile}, {name: "deprecated", path: ns.deprecatedDropInFile}, - {name: "needrestart", path: ns.needrestartConfFile}, + {name: "needrestart", path: ns.needrestartConfigFile}, } { if tt.path == "" { continue @@ -202,6 +229,52 @@ func TestWriteConfigFiles(t *testing.T) { } } +func TestHasCustomTbot(t *testing.T) { + for _, tt := range []struct { + name string + present bool + header bool + + result bool + }{ + { + name: "does not exist", + }, + { + name: "exists", + present: true, + result: true, + }, + { + name: "exists with header", + present: true, + header: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + tempdir := t.TempDir() + ns := &Namespace{ + log: slog.Default(), + tbotServiceFile: filepath.Join(tempdir, "tbot.service"), + } + err := os.MkdirAll(filepath.Dir(ns.tbotServiceFile), os.ModePerm) + require.NoError(t, err) + header := "custom" + if tt.header { + header = markerPrefix + } + if tt.present { + err = os.WriteFile(ns.tbotServiceFile, []byte(header), os.ModePerm) + require.NoError(t, err) + } + ctx := context.Background() + res, err := ns.HasCustomTbot(ctx) + require.NoError(t, err) + require.Equal(t, tt.result, res) + }) + } +} + func rebasePath(newBase, oldPath string) string { if oldPath == "" { return "" @@ -220,13 +293,15 @@ func TestNamespace_overrideFromConfig(t *testing.T) { t.Parallel() tests := []struct { - name string - cfg *unversionedTeleport - want Namespace + name string + teleportConfig *internal.UnversionedConfig + tbotConfig *internal.UnversionedConfig + customTbot bool + want Namespace }{ { name: "default", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "example.com", DataDir: "/data", }, @@ -236,8 +311,8 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, }, { - name: "empty", - cfg: &unversionedTeleport{}, + name: "empty", + teleportConfig: &internal.UnversionedConfig{}, want: Namespace{ defaultProxyAddr: "default.example.com", dataDir: "/var/lib/teleport", @@ -245,7 +320,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "full proxy", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "https://example.com:8080", }, want: Namespace{ @@ -255,7 +330,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "protocol and host", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "https://example.com", }, want: Namespace{ @@ -265,7 +340,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "host and port", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "example.com:443", }, want: Namespace{ @@ -275,7 +350,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "host", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "example.com", }, want: Namespace{ @@ -285,7 +360,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth server (v3)", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ AuthServer: "example.com", }, want: Namespace{ @@ -295,7 +370,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth server (v1/2)", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ AuthServers: []string{ "one.example.com", "two.example.com", @@ -308,7 +383,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "proxy priority", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ ProxyServer: "one.example.com", AuthServer: "two.example.com", AuthServers: []string{"three.example.com"}, @@ -320,7 +395,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth priority", - cfg: &unversionedTeleport{ + teleportConfig: &internal.UnversionedConfig{ AuthServer: "two.example.com", AuthServers: []string{"three.example.com"}, }, @@ -336,58 +411,80 @@ func TestNamespace_overrideFromConfig(t *testing.T) { dataDir: "/var/lib/teleport", }, }, + { + name: "tbot managed", + tbotConfig: &internal.UnversionedConfig{ + ProxyServer: "example.com", + }, + want: Namespace{ + defaultProxyAddr: "example.com:3080", + dataDir: "/var/lib/teleport", + }, + }, + { + name: "tbot unmanaged", + tbotConfig: &internal.UnversionedConfig{ + ProxyServer: "example.com", + }, + customTbot: true, + want: Namespace{ + defaultProxyAddr: "default.example.com", + dataDir: "/var/lib/teleport", + }, + }, + { + name: "teleport overrides tbot", + teleportConfig: &internal.UnversionedConfig{ + ProxyServer: "example.com", + DataDir: "/data", + }, + tbotConfig: &internal.UnversionedConfig{ + ProxyServer: "other.example.com", + DataDir: "/other-data", + }, + want: Namespace{ + defaultProxyAddr: "example.com:3080", + dataDir: "/data", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ns := &Namespace{ log: slog.Default(), - configFile: filepath.Join(t.TempDir(), "teleport.yaml"), defaultProxyAddr: "default.example.com", dataDir: "/var/lib/teleport", } - if tt.cfg != nil { - out, err := yaml.Marshal(unversionedConfig{Teleport: *tt.cfg}) + if tt.customTbot { + ns.tbotServiceFile = filepath.Join(t.TempDir(), "tbot.service") + err := os.WriteFile(ns.tbotServiceFile, []byte("custom"), os.ModePerm) + require.NoError(t, err) + } + if tt.teleportConfig != nil { + ns.teleportConfigFile = filepath.Join(t.TempDir(), "teleport.yaml") + out, err := yaml.Marshal(internal.UnversionedTeleport{Teleport: *tt.teleportConfig}) + require.NoError(t, err) + err = os.WriteFile(ns.teleportConfigFile, out, os.ModePerm) + require.NoError(t, err) + } + if tt.tbotConfig != nil { + ns.tbotConfigFile = filepath.Join(t.TempDir(), "tbot.yaml") + out, err := yaml.Marshal(tt.tbotConfig) require.NoError(t, err) - err = os.WriteFile(ns.configFile, out, os.ModePerm) + err = os.WriteFile(ns.tbotConfigFile, out, os.ModePerm) require.NoError(t, err) } ctx := context.Background() ns.overrideFromConfig(ctx) - ns.configFile = "" + ns.teleportConfigFile = "" + ns.tbotConfigFile = "" + ns.tbotServiceFile = "" ns.log = nil require.Equal(t, &tt.want, ns) }) } } -// In the future, the latest version of the updater may need to read a version of teleport.yaml that has -// an unsupported version which is supported by the updater-managed version of Teleport. -// This test will break if Teleport removes a field that the updater reads. -func TestUnversionedTeleportConfig(t *testing.T) { - in := unversionedConfig{ - Teleport: unversionedTeleport{ - ProxyServer: "proxy.example.com", - AuthServer: "auth.example.com", - AuthServers: []string{"auth1.example.com", "auth2.example.com"}, - DataDir: "example_dir", - }, - } - var inB bytes.Buffer - err := yaml.NewEncoder(&inB).Encode(in) - require.NoError(t, err) - fc, err := config.ReadConfig(&inB) - require.NoError(t, err) - - var outB bytes.Buffer - err = yaml.NewEncoder(&outB).Encode(fc) - require.NoError(t, err) - - var out unversionedConfig - err = yaml.NewDecoder(&outB).Decode(&out) - require.NoError(t, err) - require.Equal(t, in, out) -} - func TestWriteTeleportService(t *testing.T) { t.Parallel() @@ -423,10 +520,10 @@ func TestWriteTeleportService(t *testing.T) { t.Run(tt.name, func(t *testing.T) { serviceFile := filepath.Join(t.TempDir(), "file") ns := &Namespace{ - log: slog.Default(), - configFile: tt.configFile, - serviceFile: serviceFile, - pidFile: tt.pidFile, + log: slog.Default(), + teleportConfigFile: tt.configFile, + teleportServiceFile: serviceFile, + teleportPIDFile: tt.pidFile, } err := ns.WriteTeleportService(context.Background(), tt.pathDir, NewRevision("version", tt.flags)) require.NoError(t, err) @@ -440,6 +537,77 @@ func TestWriteTeleportService(t *testing.T) { } } +func TestWriteTbotService(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + + suffix string + pidFile string + configFile string + pathDir string + dataDir string + err bool + }{ + { + name: "default", + pidFile: "/run/tbot.pid", + configFile: "/etc/tbot.yaml", + pathDir: "/usr/local/bin", + dataDir: "/var/lib/teleport", + }, + { + name: "custom", + pidFile: "/run/tbot.pid", + configFile: "/some/path/tbot.yaml", + pathDir: "/some/path/bin", + dataDir: "/some/path", + }, + { + name: "custom suffix", + suffix: "suffix", + pidFile: "/run/tbot_suffix.pid", + configFile: "/some/path/tbot.yaml", + pathDir: "/some/path/bin", + dataDir: "/some/path", + }, + { + name: "bad pid", + pidFile: "/some/path/tbot.pid", + configFile: "/some/path/tbot.yaml", + pathDir: "/some/path/bin", + dataDir: "/some/path", + err: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + serviceFile := filepath.Join(t.TempDir(), "file") + ns := &Namespace{ + log: slog.Default(), + name: tt.suffix, + tbotConfigFile: tt.configFile, + tbotServiceFile: serviceFile, + tbotPIDFile: tt.pidFile, + dataDir: tt.dataDir, + } + err := ns.WriteTbotService(context.Background(), tt.pathDir, NewRevision("version", 0)) + if tt.err { + require.Error(t, err) + return + } + require.NoError(t, err) + data, err := os.ReadFile(serviceFile) + require.NoError(t, err) + if golden.ShouldSet() { + golden.Set(t, data) + } + require.Equal(t, string(golden.Get(t)), string(data)) + }) + } +} + func TestReplaceTeleportService(t *testing.T) { t.Parallel() @@ -498,9 +666,9 @@ WantedBy=multi-user.target for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ns := &Namespace{ - log: slog.Default(), - configFile: tt.configFile, - pidFile: tt.pidFile, + log: slog.Default(), + teleportConfigFile: tt.configFile, + teleportPIDFile: tt.pidFile, } data := ns.ReplaceTeleportService([]byte(tt.in), tt.pathDir, tt.flags) if golden.ShouldSet() { diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden new file mode 100644 index 0000000000000..0a42425a845c0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden @@ -0,0 +1,12 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden new file mode 100644 index 0000000000000..d7028f84581d7 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden new file mode 100644 index 0000000000000..fb2c1f4c3156e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden @@ -0,0 +1,15 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /path + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden new file mode 100644 index 0000000000000..42a4ebb9cedb4 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden @@ -0,0 +1,15 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /path + group: new-group + base_url: https://example.com/new + enabled: true + pinned: false +status: + id_file: updater-id-file + active: + version: new-version + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden new file mode 100644 index 0000000000000..c2342d86c90e9 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden new file mode 100644 index 0000000000000..69b08b9c83c9d --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: "" + path: "" + base_url: http://example.com + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden new file mode 100644 index 0000000000000..6e104086250e3 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden @@ -0,0 +1,10 @@ +version: v1 +kind: update_config +spec: + proxy: "" + path: "" + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden new file mode 100644 index 0000000000000..13cbb0b358e62 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden @@ -0,0 +1,14 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false + selinux_ssh: true +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden new file mode 100644 index 0000000000000..13cbb0b358e62 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden @@ -0,0 +1,14 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false + selinux_ssh: true +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden new file mode 100644 index 0000000000000..0c3dcaac8edbd --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden @@ -0,0 +1,10 @@ +version: "" +kind: "" +spec: + proxy: "" + path: "" + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden new file mode 100644 index 0000000000000..e16d92d41752e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden new file mode 100644 index 0000000000000..e16d92d41752e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden new file mode 100644 index 0000000000000..c2342d86c90e9 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden new file mode 100644 index 0000000000000..c2342d86c90e9 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden new file mode 100644 index 0000000000000..067fcf60bd527 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden @@ -0,0 +1,10 @@ +version: v1 +kind: update_config +spec: + proxy: "" + path: "" + enabled: false + pinned: false +status: + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden new file mode 100644 index 0000000000000..6e104086250e3 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden @@ -0,0 +1,10 @@ +version: v1 +kind: update_config +spec: + proxy: "" + path: "" + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden new file mode 100644 index 0000000000000..6e104086250e3 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden @@ -0,0 +1,10 @@ +version: v1 +kind: update_config +spec: + proxy: "" + path: "" + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden new file mode 100644 index 0000000000000..e16d92d41752e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden new file mode 100644 index 0000000000000..e16d92d41752e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: false + pinned: false +status: + id_file: updater-id-file + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden new file mode 100644 index 0000000000000..14a0b83b8539e --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden @@ -0,0 +1,22 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + flags: [Enterprise, FIPS] + active: + version: 16.3.0 + flags: [Enterprise, FIPS] + backup: + version: old-version + flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden new file mode 100644 index 0000000000000..667353a7c24ae --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden @@ -0,0 +1,18 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: false + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: old-version + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden new file mode 100644 index 0000000000000..d257cd6c30282 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: 16.3.0 + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden new file mode 100644 index 0000000000000..7697a84f3326c --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden @@ -0,0 +1,19 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden new file mode 100644 index 0000000000000..297b00ce4ecf8 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: http://example.com + enabled: true + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden new file mode 100644 index 0000000000000..575463dc75100 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden @@ -0,0 +1,16 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: false + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden new file mode 100644 index 0000000000000..a771da1fc9e25 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden @@ -0,0 +1,10 @@ +version: "" +kind: "" +spec: + proxy: localhost + path: "" + enabled: false + pinned: false +status: + active: + version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden new file mode 100644 index 0000000000000..f29735ca0230b --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden @@ -0,0 +1,12 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: "" + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden new file mode 100644 index 0000000000000..501506cf96c78 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden @@ -0,0 +1,13 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: true +status: + active: + version: old-version + backup: + version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden new file mode 100644 index 0000000000000..170e821a409b2 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden @@ -0,0 +1,21 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: false + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: old-version + backup: + version: backup-version + skip: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden new file mode 100644 index 0000000000000..10b36430ffed1 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden @@ -0,0 +1,15 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: old-version + backup: + version: backup-version + skip: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden new file mode 100644 index 0000000000000..b6b43595a5903 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden @@ -0,0 +1,12 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: false + pinned: false +status: + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden new file mode 100644 index 0000000000000..b6b43595a5903 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden @@ -0,0 +1,12 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: false + pinned: false +status: + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden new file mode 100644 index 0000000000000..efdd858023547 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden @@ -0,0 +1,20 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden new file mode 100644 index 0000000000000..efdd858023547 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden @@ -0,0 +1,20 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden new file mode 100644 index 0000000000000..efdd858023547 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden @@ -0,0 +1,20 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden new file mode 100644 index 0000000000000..a4cac37b8733c --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden @@ -0,0 +1,12 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + group: group + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=false.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=false.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=true.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=true.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden new file mode 100644 index 0000000000000..926667a2e7fc0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden new file mode 100644 index 0000000000000..926667a2e7fc0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden @@ -0,0 +1,11 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + active: + version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden new file mode 100644 index 0000000000000..7697a84f3326c --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden @@ -0,0 +1,19 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden new file mode 100644 index 0000000000000..7697a84f3326c --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden @@ -0,0 +1,19 @@ +version: v1 +kind: update_config +spec: + proxy: localhost + path: /usr/local/bin + base_url: https://example.com + enabled: true + pinned: false +status: + id_file: updater-id-file + last_update: + success: true + time: 2025-01-01T00:00:00Z + target: + version: 16.3.0 + active: + version: 16.3.0 + backup: + version: old-version diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden index b5d6a74435cb2..85b19ed507a35 100644 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden @@ -1 +1,2 @@ $nrconf{override_rc}{qr(^teleport\.service)} = 0; +$nrconf{override_rc}{qr(^tbot\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden index ad6bd606a74cb..4994533b2e2cd 100644 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden @@ -1 +1,2 @@ $nrconf{override_rc}{qr(^teleport_test\.service)} = 0; +$nrconf{override_rc}{qr(^tbot_test\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden new file mode 100644 index 0000000000000..fcaaae54ce5d0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden @@ -0,0 +1,6 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Service] +ExecStart= +ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden new file mode 100644 index 0000000000000..4ca6b61b76342 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden @@ -0,0 +1,6 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Service] +Environment="TELEPORT_UPDATE_CONFIG_FILE=/opt/teleport/default/update.yaml" +Environment="TELEPORT_UPDATE_INSTALL_DIR=/opt/teleport" diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden new file mode 100644 index 0000000000000..b5d6a74435cb2 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden @@ -0,0 +1 @@ +$nrconf{override_rc}{qr(^teleport\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden new file mode 100644 index 0000000000000..e3038b3255fc0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden @@ -0,0 +1,9 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Unit] +Description=Teleport auto-update service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/teleport-update --install-suffix= "--install-dir=/opt/teleport" update diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden new file mode 100644 index 0000000000000..df921a1e883f1 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden @@ -0,0 +1,13 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Unit] +Description=Teleport auto-update timer unit + +[Timer] +OnActiveSec=1m +OnUnitActiveSec=5m +RandomizedDelaySec=1m + +[Install] +WantedBy=teleport.service diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden new file mode 100644 index 0000000000000..fcaaae54ce5d0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden @@ -0,0 +1,6 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Service] +ExecStart= +ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden new file mode 100644 index 0000000000000..4ca6b61b76342 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden @@ -0,0 +1,6 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Service] +Environment="TELEPORT_UPDATE_CONFIG_FILE=/opt/teleport/default/update.yaml" +Environment="TELEPORT_UPDATE_INSTALL_DIR=/opt/teleport" diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden new file mode 100644 index 0000000000000..b5d6a74435cb2 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden @@ -0,0 +1 @@ +$nrconf{override_rc}{qr(^teleport\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden new file mode 100644 index 0000000000000..e3038b3255fc0 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden @@ -0,0 +1,9 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Unit] +Description=Teleport auto-update service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/teleport-update --install-suffix= "--install-dir=/opt/teleport" update diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden new file mode 100644 index 0000000000000..df921a1e883f1 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden @@ -0,0 +1,13 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +[Unit] +Description=Teleport auto-update timer unit + +[Timer] +OnActiveSec=1m +OnUnitActiveSec=5m +RandomizedDelaySec=1m + +[Install] +WantedBy=teleport.service diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden new file mode 100644 index 0000000000000..cff076375ac53 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden @@ -0,0 +1,22 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +# Generated by `tbot` +[Unit] +Description=tbot - Teleport Machine ID Service +After=network.target + +[Service] +Type=simple +User=root +Group=root +Restart=always +RestartSec=5 +Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" +ExecStart=/some/path/bin/tbot start -c /some/path/tbot.yaml --diag-socket-for-updater=/some/path/bot/debug.sock --pid-file=/run/tbot.pid +ExecReload=/bin/kill -HUP $MAINPID +PIDFile=/run/tbot.pid +LimitNOFILE=524288 + +[Install] +WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden new file mode 100644 index 0000000000000..37334d016ae23 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden @@ -0,0 +1,22 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +# Generated by `tbot` +[Unit] +Description=tbot_suffix - Teleport Machine ID Service +After=network.target + +[Service] +Type=simple +User=root +Group=root +Restart=always +RestartSec=5 +Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" +ExecStart=/some/path/bin/tbot start -c /some/path/tbot.yaml --diag-socket-for-updater=/some/path/bot/debug.sock --pid-file=/run/tbot_suffix.pid +ExecReload=/bin/kill -HUP $MAINPID +PIDFile=/run/tbot_suffix.pid +LimitNOFILE=524288 + +[Install] +WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden new file mode 100644 index 0000000000000..c269618ba3484 --- /dev/null +++ b/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden @@ -0,0 +1,22 @@ +# teleport-update version +# DO NOT EDIT THIS FILE + +# Generated by `tbot` +[Unit] +Description=tbot - Teleport Machine ID Service +After=network.target + +[Service] +Type=simple +User=root +Group=root +Restart=always +RestartSec=5 +Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" +ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --diag-socket-for-updater=/var/lib/teleport/bot/debug.sock --pid-file=/run/tbot.pid +ExecReload=/bin/kill -HUP $MAINPID +PIDFile=/run/tbot.pid +LimitNOFILE=524288 + +[Install] +WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/updater.go b/lib/autoupdate/agent/updater.go index cb06d96b94d6c..25eb6e8e92836 100644 --- a/lib/autoupdate/agent/updater.go +++ b/lib/autoupdate/agent/updater.go @@ -59,6 +59,8 @@ const ( SetupVersionEnvVar = "TELEPORT_UPDATE_SETUP_VERSION" // SetupFlagsEnvVar specifies Teleport version flags. SetupFlagsEnvVar = "TELEPORT_UPDATE_SETUP_FLAGS" + // SetupTbotEnvVar specifies that the updater should manage tbot. + SetupTbotEnvVar = "TELEPORT_UPDATE_SETUP_TBOT" // SetupSELinuxSSHEnvVar is the environment variable that enables SELinux SSH support. SetupSELinuxSSHEnvVar = "TELEPORT_UPDATE_SELINUX_SSH" ) @@ -126,27 +128,35 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { cfg.SystemDir = packageSystemDir } validator := Validator{Log: cfg.Log} - debugClient := debug.NewClient(ns.dataDir) + teleportDebugClient := debug.NewClient(ns.dataDir) + // Teleport's debug client happens to work for tbot, but this may not + // be intentional. In the future, we might consider extracting a generic + // debug client that can be used in both contexts. + tbotDebugClient := debug.NewClient(filepath.Join(ns.dataDir, "bot")) + return &Updater{ - Log: cfg.Log, - Pool: certPool, - InsecureSkipVerify: cfg.InsecureSkipVerify, - UpdateConfigFile: filepath.Join(ns.Dir(), updateConfigName), - UpdateIDFile: ns.updaterIDFile, - MachineIDFile: systemdMachineIDFile, - TeleportIDFile: filepath.Join(ns.dataDir, teleportHostIDFileName), - TeleportConfigFile: ns.configFile, - TeleportServiceName: filepath.Base(ns.serviceFile), - DefaultProxyAddr: ns.defaultProxyAddr, - DefaultPathDir: ns.defaultPathDir, + Log: cfg.Log, + Pool: certPool, + InsecureSkipVerify: cfg.InsecureSkipVerify, + UpdateConfigFile: filepath.Join(ns.Dir(), updateConfigName), + UpdateIDFile: ns.updaterIDFile, + MachineIDFile: systemdMachineIDFile, + TeleportIDFile: filepath.Join(ns.dataDir, teleportHostIDFileName), + TeleportConfigFile: ns.teleportConfigFile, + DefaultProxyAddr: ns.defaultProxyAddr, + DefaultPathDir: ns.defaultPathDir, Installer: &LocalInstaller{ InstallDir: filepath.Join(ns.Dir(), versionsDirName), TargetServices: []ServiceFile{ { - Path: ns.serviceFile, + Path: ns.teleportServiceFile, Binary: "teleport", - ExampleName: serviceName, - ExampleFunc: ns.ReplaceTeleportService, + ExampleName: teleportServiceName, + ExampleFunc: ns.ReplaceTeleportService, // required for TryLinkSystem + }, + { + Path: ns.tbotServiceFile, + Binary: "tbot", }, }, SystemBinDir: filepath.Join(cfg.SystemDir, "bin"), @@ -158,14 +168,23 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { ValidateBinary: validator.IsBinary, Template: autoupdate.DefaultCDNURITemplate, }, - Process: &SystemdService{ - ServiceName: filepath.Base(ns.serviceFile), - PIDFile: ns.pidFile, - Ready: debugClient, + TeleportProcess: &SystemdService{ + ServiceName: filepath.Base(ns.teleportServiceFile), + PIDFile: ns.teleportPIDFile, + Ready: teleportDebugClient, Log: cfg.Log, }, + TbotProcess: &SystemdService{ + ServiceName: filepath.Base(ns.tbotServiceFile), + PIDFile: ns.tbotPIDFile, + Ready: tbotDebugClient, + Log: cfg.Log, + ForceRestart: true, + }, WriteTeleportService: ns.WriteTeleportService, - ReexecSetup: func(ctx context.Context, pathDir string, rev Revision, enableSELinux, reload bool) error { + WriteTbotService: ns.WriteTbotService, + HasCustomTbot: ns.HasCustomTbot, + ReexecSetup: func(ctx context.Context, pathDir string, rev Revision, enableSELinux, reload, tbot bool) error { name := filepath.Join(pathDir, BinaryName) if cfg.SelfSetup && runtime.GOOS == constants.LinuxOS { name = "/proc/self/exe" @@ -187,11 +206,13 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { cmd := exec.CommandContext(ctx, name, args...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout + // Never set env vars conditionally, as they may be set in the parent environment. cmd.Env = append(slices.Clone(os.Environ()), SetupVersionEnvVar+"="+rev.Version, SetupFlagsEnvVar+"="+strings.Join(rev.Flags.Strings(), "\n"), + SetupSELinuxSSHEnvVar+"="+strconv.FormatBool(enableSELinux), + SetupTbotEnvVar+"="+strconv.FormatBool(tbot), ) - cmd.Env = append(cmd.Env, SetupSELinuxSSHEnvVar+"="+strconv.FormatBool(enableSELinux)) cfg.Log.InfoContext(ctx, "Executing new teleport-update binary to update configuration.") defer cfg.Log.InfoContext(ctx, "Finished executing new teleport-update binary.") return trace.Wrap(cmd.Run()) @@ -242,23 +263,28 @@ type Updater struct { TeleportIDFile string // TeleportConfigFile contains the path to Teleport's configuration. TeleportConfigFile string - // TeleportServiceName contains the full name of the systemd service for Teleport - TeleportServiceName string // DefaultProxyAddr contains Teleport's proxy address. This may differ from the updater's. DefaultProxyAddr string // DefaultPathDir contains the default path that Teleport binaries should be installed into. DefaultPathDir string // Installer manages installations of the Teleport agent. Installer Installer - // Process manages a running instance of Teleport. - Process Process + // TeleportProcess manages a running instance of Teleport. + TeleportProcess Process + // TbotProcess manages a running instance of tbot. + TbotProcess Process // WriteTeleportService writes the teleport systemd service for the version of Teleport // matching the currently running updater. WriteTeleportService func(ctx context.Context, path string, rev Revision) error + // WriteTbotService writes the tbot systemd service for the version of Teleport + // matching the currently running updater. + WriteTbotService func(ctx context.Context, path string, rev Revision) error + // HasCustomTbot returns true if a non-updater-managed tbot installation is present. + HasCustomTbot func(ctx context.Context) (bool, error) // ReexecSetup re-execs teleport-update with the setup command. // This configures an SELinux module, configures the updater service, // verifies the installation, and optionally reloads Teleport. - ReexecSetup func(ctx context.Context, path string, rev Revision, installSELinux, reload bool) error + ReexecSetup func(ctx context.Context, path string, rev Revision, installSELinux, reload, tbot bool) error // SetupNamespace configures the Teleport updater service for the current Namespace // and configures an SELinux module. SetupNamespace func(ctx context.Context, path string, rev Revision, installSELinux bool) error @@ -309,8 +335,6 @@ type Installer interface { var ( // ErrLinked is returned when a linked version cannot be operated on. ErrLinked = errors.New("version is linked") - // ErrNotNeeded is returned when the operation is not needed. - ErrNotNeeded = errors.New("not needed") // ErrNotSupported is returned when the operation is not supported on the platform. ErrNotSupported = errors.New("not supported on this platform") // ErrNotAvailable is returned when the operation is not available at the current version of the platform. @@ -325,6 +349,8 @@ var ( // Process provides an API for interacting with a running Teleport process. type Process interface { + // Name of the process. + Name() string // Reload must reload the Teleport process as gracefully as possible. // If the process is not healthy after reloading, Reload must return an error. // If the process did not require reloading, Reload must return ErrNotNeeded. @@ -474,7 +500,8 @@ func (u *Updater) Install(ctx context.Context, override OverrideConfig) (err err } u.Log.InfoContext(ctx, "Configuration updated.") u.LogConfigWarnings(ctx, cfg.Spec.Path) - u.notices(ctx) + u.teleportNotices(ctx) + u.tbotNotices(ctx) return nil } @@ -523,7 +550,7 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { // it is clear we are not going to recover the package's systemd service if it // was overwritten. if filepath.Clean(cfg.Spec.Path) != filepath.Clean(defaultPathDir) { - if u.TeleportServiceName == serviceName { + if u.TeleportProcess.Name() == teleportServiceName { if !force { u.Log.ErrorContext(ctx, "Default Teleport systemd service would be removed, and --force was not passed.") u.Log.ErrorContext(ctx, "Refusing to remove Teleport from this system.") @@ -554,12 +581,27 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { u.Log.InfoContext(ctx, "Updater-managed installation of Teleport detected.") u.Log.InfoContext(ctx, "Restoring packaged version of Teleport before removing.") + // Restart teleport and tbot. + // If a tbot service is present, and it was created by the updater, + // then we reuse the service for the package to avoid disruption. + + customTbot, err := u.HasCustomTbot(ctx) + if err != nil { + customTbot = true + u.Log.ErrorContext(ctx, "Failed to determine if a custom tbot service is installed.", errorKey, err) + u.Log.ErrorContext(ctx, "Tbot will not be restarted by the updater after the package is restored.") + } + pg := ProcessGroup{u.TeleportProcess} + if !customTbot { + pg = append(pg, u.TbotProcess) + } + revertConfig := func(ctx context.Context) bool { if ok := revert(ctx); !ok { u.Log.ErrorContext(ctx, "Failed to revert Teleport symlinks. Installation likely broken.") return false } - if err := u.Process.Sync(ctx); err != nil { + if err := pg.Sync(ctx); err != nil { u.Log.ErrorContext(ctx, "Failed to revert systemd configuration after failed restart.", errorKey, err) return false } @@ -568,7 +610,7 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { // Sync systemd. - err = u.Process.Sync(ctx) + err = pg.Sync(ctx) if errors.Is(err, context.Canceled) { return trace.Errorf("sync canceled") } @@ -583,22 +625,19 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { return trace.Wrap(err, "failed to validate configuration for system package version of Teleport") } - // Restart Teleport. - u.Log.InfoContext(ctx, "Teleport package successfully restored.") - err = u.Process.Reload(ctx) + err = pg.Reload(ctx) if errors.Is(err, context.Canceled) { return trace.Errorf("reload canceled") } if err != nil && - !errors.Is(err, ErrNotNeeded) && // no output if restart not needed !errors.Is(err, ErrNotSupported) { // already logged above for Sync - // If reloading Teleport at the new version fails, revert and reload. + // If reloading at the new version fails, revert and reload again. u.Log.ErrorContext(ctx, "Reverting symlinks due to failed restart.") if ok := revertConfig(ctx); ok { - if err := u.Process.Reload(ctx); err != nil && !errors.Is(err, ErrNotNeeded) { - u.Log.ErrorContext(ctx, "Failed to reload Teleport after reverting.", errorKey, err) + if err := pg.Reload(ctx); err != nil { + u.Log.ErrorContext(ctx, "Failed to reload after reverting.", errorKey, err, "service", pg.Name()) u.Log.ErrorContext(ctx, "Installation likely broken.") } else { u.Log.WarnContext(ctx, "Teleport updater detected an error with the new installation and successfully reverted it.") @@ -617,12 +656,16 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { func (u *Updater) removeWithoutSystem(ctx context.Context, cfg *UpdateConfig) error { u.Log.InfoContext(ctx, "Updater-managed installation of Teleport detected.") u.Log.InfoContext(ctx, "Attempting to unlink and remove.") - ok, err := u.Process.IsActive(ctx) + + // Note: tbot.service could be from an unrelated installation, but we should still fail to be safe. + // This would only be an issue for primary installations on PATH, where removal will likely break tbot. + pg := ProcessGroup{u.TeleportProcess, u.TbotProcess} + ok, err := pg.IsActive(ctx) if err != nil && !errors.Is(err, ErrNotSupported) { return trace.Wrap(err) } if ok { - return trace.Errorf("refusing to remove active installation of Teleport, please stop and disable Teleport first") + return trace.Errorf("refusing to remove active installation, please stop and disable %s first", pg.Name()) } if err := u.Installer.Unlink(ctx, cfg.Status.Active, cfg.Spec.Path); err != nil { return trace.Wrap(err) @@ -906,9 +949,10 @@ func (u *Updater) Update(ctx context.Context, now bool) error { } else { u.Log.InfoContext(ctx, "Configuration updated.") } - // Show notices last + // Show teleportNotices last if updateErr == nil && now { - u.notices(ctx) + u.teleportNotices(ctx) + u.tbotNotices(ctx) } return trace.NewAggregate(updateErr, writeErr) } @@ -972,7 +1016,7 @@ func (u *Updater) removeRevision(ctx context.Context, cfg *UpdateConfig, rev Rev return trace.Wrap(u.Installer.Remove(ctx, rev)) } -func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision, force, agpl bool) error { +func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision, overwrite, agpl bool) error { baseURL := cfg.Spec.BaseURL if baseURL == "" { if agpl { @@ -1013,11 +1057,31 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // Cleanup logic at the end of this function will ensure that they are removed // eventually. - revert, err := u.Installer.Link(ctx, target, cfg.Spec.Path, force) + revert, err := u.Installer.Link(ctx, target, cfg.Spec.Path, overwrite) if err != nil { return trace.Wrap(err, "failed to link") } + ignoreTbot := false + if customTbot, err := u.HasCustomTbot(ctx); err != nil { + u.Log.ErrorContext(ctx, "Failed to determine if a custom tbot service is installed.", errorKey, err) + u.Log.ErrorContext(ctx, "Tbot will not be managed by the updater.") + ignoreTbot = true + } else if overwrite && customTbot { + u.Log.WarnContext(ctx, "Custom tbot service will be overwritten by the updater.") + ignoreTbot = false + } else if customTbot { + u.Log.WarnContext(ctx, "Unable to manage tbot process due to custom tbot.service file.") + u.Log.WarnContext(ctx, "Pass --overwrite to delete and replace tbot.service with an updater-managed copy.") + u.Log.InfoContext(ctx, "The updater-managed copy may still be modified using systemd drop-in files.") + ignoreTbot = true + } + + pg := ProcessGroup{u.TeleportProcess} + if !ignoreTbot { + pg = append(pg, u.TbotProcess) + } + // If we fail to revert after this point, the next update/enable will // fix the link to restore the active version. @@ -1025,7 +1089,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision if target.Version != "" { cfg.Status.Skip = toPtr(target) } - if force { + if overwrite { u.Log.ErrorContext(ctx, "Unable to revert Teleport symlinks in overwrite mode. Installation likely broken.") return false } @@ -1044,7 +1108,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If re-linking the same version, do not attempt to restart services. if cfg.Status.Active == target { - err := u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, false) + err := u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, false, !ignoreTbot) if errors.Is(err, context.Canceled) { return trace.Errorf("check canceled") } @@ -1065,7 +1129,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If a new version was linked, restart services (including on revert). - err = u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, true) + err = u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, true, !ignoreTbot) if errors.Is(err, context.Canceled) { return trace.Errorf("check canceled") } @@ -1073,8 +1137,8 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If reloading Teleport at the new version fails, revert and reload. u.Log.ErrorContext(ctx, "Reverting symlinks due to failed restart.") if ok := revertConfig(ctx); ok { - if err := u.Process.Reload(ctx); err != nil && !errors.Is(err, ErrNotNeeded) { - u.Log.ErrorContext(ctx, "Failed to reload Teleport after reverting. Installation likely broken.", errorKey, err) + if err := pg.Reload(ctx); err != nil { + u.Log.ErrorContext(ctx, "Failed to reload services after reverting. Installation likely broken.", errorKey, err) } else { u.Log.WarnContext(ctx, "Teleport updater detected an error with the new installation and successfully reverted it.") } @@ -1096,14 +1160,20 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // Setup writes updater configuration and verifies the Teleport installation. // If restart is true, Setup also restarts Teleport. // Setup is safe to run concurrently with other Updater commands. -func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installSELinux, restart bool) error { - - // Write Teleport systemd service. +func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installSELinux, restart, tbot bool) error { + // Write teleport systemd service. if err := u.WriteTeleportService(ctx, path, rev); err != nil { return trace.Wrap(err, "failed to write teleport systemd service") } + // Write tbot systemd service. + if tbot { + if err := u.WriteTbotService(ctx, path, rev); err != nil { + return trace.Wrap(err, "failed to write teleport systemd service") + } + } + // Setup teleport-updater configuration and sync systemd. err := u.SetupNamespace(ctx, path, rev, installSELinux) if errors.Is(err, context.Canceled) { @@ -1114,41 +1184,68 @@ func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installS } // Validations - - present, err := u.Process.IsPresent(ctx) - if errors.Is(err, context.Canceled) { - return trace.Errorf("config check canceled") + teleportPresent, err := u.validateProcess(ctx, u.TeleportProcess) + if err != nil { + return trace.Wrap(err) } - if errors.Is(err, ErrNotSupported) { - u.Log.WarnContext(ctx, "Skipping all systemd setup because systemd is not running.") + tbotPresent, err := u.validateProcess(ctx, u.TbotProcess) + if err != nil { + return trace.Wrap(err) + } + + // Restart Teleport only if necessary. + if !restart { return nil } - if errors.Is(err, ErrNotAvailable) { - u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping SELinux verification.") - } else if err != nil { - return trace.Wrap(err, "failed to determine if new version of Teleport has an installed systemd service") - } else if !present { - return trace.Errorf("cannot find systemd service for new version of Teleport, check SELinux settings") + + if !teleportPresent || !tbotPresent { + u.Log.ErrorContext(ctx, "Missing services will not be restarted.") } - // Restart Teleport if necessary. + var pg ProcessGroup + if teleportPresent { + pg = append(pg, u.TeleportProcess) + } + if tbotPresent && tbot { + pg = append(pg, u.TbotProcess) + } - if restart { - err = u.Process.Reload(ctx) - if errors.Is(err, context.Canceled) { - return trace.Errorf("reload canceled") - } - if err != nil && - !errors.Is(err, ErrNotNeeded) { // skip if not needed - return trace.Wrap(err, "failed to reload Teleport") - } + err = pg.Reload(ctx) + if errors.Is(err, context.Canceled) { + return trace.Errorf("reload canceled") + } + if err != nil { + return trace.Wrap(err) } return nil } -// notices displays final notices after install or update. -func (u *Updater) notices(ctx context.Context) { - enabled, err := u.Process.IsEnabled(ctx) +func (u *Updater) validateProcess(ctx context.Context, p Process) (bool, error) { + present, err := p.IsPresent(ctx) + if errors.Is(err, context.Canceled) { + return false, trace.Errorf("config check canceled") + } + if errors.Is(err, ErrNotSupported) { + u.Log.WarnContext(ctx, "Skipping systemd setup because systemd is not running.", "service", p.Name()) + return false, nil + } + if errors.Is(err, ErrNotAvailable) { + u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping service verification.", "service", p.Name()) + return true, nil + } + if err != nil { + return false, trace.Wrap(err, "failed to determine if the new version has an installed systemd service", "service", p.Name()) + } + if !present { + u.Log.ErrorContext(ctx, "Cannot find systemd service for the new version, check SELinux settings.", "service", p.Name()) + return false, nil + } + return true, nil +} + +// teleportNotices displays final notices for teleport after install or update. +func (u *Updater) teleportNotices(ctx context.Context) { + enabled, err := u.TeleportProcess.IsEnabled(ctx) if errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Teleport is installed, but systemd is not present to start it.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, your system must also be configured to start Teleport.") @@ -1162,7 +1259,7 @@ func (u *Updater) notices(ctx context.Context) { u.Log.ErrorContext(ctx, "Failed to determine if Teleport is enabled.", errorKey, err) return } - active, err := u.Process.IsActive(ctx) + active, err := u.TeleportProcess.IsActive(ctx) if err != nil { u.Log.ErrorContext(ctx, "Failed to determine if Teleport is active.", errorKey, err) return @@ -1170,17 +1267,52 @@ func (u *Updater) notices(ctx context.Context) { if !enabled && active { u.Log.WarnContext(ctx, "Teleport is installed and started, but not configured to start on boot.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must enable it.", - "command", "systemctl enable "+u.TeleportServiceName) + "command", "systemctl enable "+u.TeleportProcess.Name()) } if !active && enabled { u.Log.WarnContext(ctx, "Teleport is installed and enabled at boot, but not running.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must start it.", - "command", "systemctl start "+u.TeleportServiceName) + "command", "systemctl start "+u.TeleportProcess.Name()) } if !active && !enabled { u.Log.WarnContext(ctx, "Teleport is installed, but not running or enabled at boot.") - u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must enable and start.", - "command", "systemctl enable --now "+u.TeleportServiceName) + u.Log.WarnContext(ctx, "To enable and start Teleport, configure teleport.yaml and run systemctl.", + "command", "systemctl enable --now "+u.TeleportProcess.Name()) + } +} + +// tbotNotices displays final notices for tbot after install or update. +func (u *Updater) tbotNotices(ctx context.Context) { + enabled, err := u.TbotProcess.IsEnabled(ctx) + if errors.Is(err, ErrNotSupported) || + errors.Is(err, ErrNotAvailable) { + // Ignore for tbot, as the corresponding teleport error will cover it. + return + } + if err != nil { + u.Log.ErrorContext(ctx, "Failed to determine if tbot is enabled.", errorKey, err) + return + } + active, err := u.TbotProcess.IsActive(ctx) + if err != nil { + u.Log.ErrorContext(ctx, "Failed to determine if tbot is active.", errorKey, err) + return + } + if !enabled && active { + u.Log.InfoContext(ctx, "The tbot MachineID client installed and started, but not configured to start on boot.") + u.Log.WarnContext(ctx, "After configuring tbot.yaml, you must enable it.", + "command", "systemctl enable "+u.TbotProcess.Name()) + } + if !active && enabled { + u.Log.WarnContext(ctx, "The tbot MachineID client is installed and enabled at boot, but not running.") + u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must start it.", + "command", "systemctl start "+u.TbotProcess.Name()) + } + if !active && !enabled { + // Info-level as many installations will be agent-only. + u.Log.InfoContext(ctx, "The tbot MachineID client installed, but not running or enabled at boot.") + u.Log.InfoContext(ctx, "To enable and start tbot, configure tbot.yaml and run systemctl.", + "command", "systemctl enable --now "+u.TbotProcess.Name()) } } @@ -1248,19 +1380,22 @@ func (u *Updater) LinkPackage(ctx context.Context) error { // If syncing succeeds, ensure the installed systemd service can be found via systemctl. // SELinux contexts can interfere with systemctl's ability to read service files. - if err := u.Process.Sync(ctx); errors.Is(err, ErrNotSupported) { + err = u.TeleportProcess.Sync(ctx) + if errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Systemd is not installed. Skipping sync.") - } else if err != nil { + u.Log.InfoContext(ctx, "Successfully linked system package installation.") + return nil + } + if err != nil { return trace.Wrap(err, "failed to sync systemd configuration") - } else { - present, err := u.Process.IsPresent(ctx) - if errors.Is(err, ErrNotAvailable) { - u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping SELinux verification.") - } else if err != nil { - return trace.Wrap(err, "failed to determine if Teleport has an installed systemd service") - } else if !present { - return trace.Errorf("cannot find systemd service for Teleport, check SELinux settings") - } + } + + present, err := u.validateProcess(ctx, u.TeleportProcess) + if err != nil { + return trace.Wrap(err) + } + if !present { + return trace.Errorf("missing Teleport service") } u.Log.InfoContext(ctx, "Successfully linked system package installation.") return nil @@ -1272,7 +1407,7 @@ func (u *Updater) UnlinkPackage(ctx context.Context) error { if err := u.Installer.UnlinkSystem(ctx); err != nil { return trace.Wrap(err, "failed to unlink system package installation") } - if err := u.Process.Sync(ctx); errors.Is(err, ErrNotSupported) { + if err := u.TeleportProcess.Sync(ctx); errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Systemd is not installed. Skipping sync.") } else if err != nil { return trace.Wrap(err, "failed to sync systemd configuration") diff --git a/lib/autoupdate/agent/updater_test.go b/lib/autoupdate/agent/updater_test.go index bda5ea6c4b0d3..20fff13c97717 100644 --- a/lib/autoupdate/agent/updater_test.go +++ b/lib/autoupdate/agent/updater_test.go @@ -730,157 +730,189 @@ func TestUpdater_Update(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var requestedGroup string - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestedGroup = r.URL.Query().Get("group") - config := webclient.PingResponse{ - AutoUpdate: webclient.AutoUpdateSettings{ - AgentVersion: "16.3.0", - AgentAutoUpdate: tt.inWindow, - }, - } - config.Edition = "community" - if tt.flags&autoupdate.FlagEnterprise != 0 { - config.Edition = "ent" - } - if tt.agpl { - config.Edition = "oss" + for _, tbot := range []bool{false, true} { + t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { + var requestedGroup string + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestedGroup = r.URL.Query().Get("group") + config := webclient.PingResponse{ + AutoUpdate: webclient.AutoUpdateSettings{ + AgentVersion: "16.3.0", + AgentAutoUpdate: tt.inWindow, + }, + } + config.Edition = "community" + if tt.flags&autoupdate.FlagEnterprise != 0 { + config.Edition = "ent" + } + if tt.agpl { + config.Edition = "oss" + } + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 + err := json.NewEncoder(w).Encode(config) + require.NoError(t, err) + })) + t.Cleanup(server.Close) + + dir := t.TempDir() + ns := &Namespace{ + installDir: dir, + defaultPathDir: "ignored", + updaterIDFile: "updater-id-file", } - config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 - err := json.NewEncoder(w).Encode(config) + _, err := ns.Init() require.NoError(t, err) - })) - t.Cleanup(server.Close) - - dir := t.TempDir() - ns := &Namespace{ - installDir: dir, - defaultPathDir: "ignored", - updaterIDFile: "updater-id-file", - } - _, err := ns.Init() - require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) - - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) - // Create config file only if provided in test case - if tt.cfg != nil { - tt.cfg.Spec.Proxy = strings.TrimPrefix(server.URL, "https://") - b, err := yaml.Marshal(tt.cfg) + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) - require.NoError(t, err) - } - var ( - installedRevision Revision - installedBaseURL string - linkedRevision Revision - removedRevisions []Revision - revertFuncCalls int - setupCalls int - revertSetupCalls int - reloadCalls int - ) - updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { - for _, r := range tt.linkedRevisions { - if r == rev { - require.False(t, force) + // Create config file only if provided in test case + if tt.cfg != nil { + tt.cfg.Spec.Proxy = strings.TrimPrefix(server.URL, "https://") + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } + + var ( + installedRevision Revision + installedBaseURL string + linkedRevision Revision + removedRevisions []Revision + revertFuncCalls int + setupCalls int + revertSetupCalls int + teleportReloadCalls int + tbotReloadCalls int + ) + updater.Installer = &testInstaller{ + FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { + for _, r := range tt.linkedRevisions { + if r == rev { + require.False(t, force) + } } - } - installedRevision = rev - installedBaseURL = baseURL - return tt.installErr - }, - FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { - linkedRevision = rev - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, nil - }, - FuncList: func(_ context.Context) (revs []Revision, err error) { - return slices.Compact([]Revision{ - installedRevision, - tt.cfg.Status.Active, - NewRevision("unknown-version", 0), - }), nil - }, - FuncRemove: func(_ context.Context, rev Revision) error { - removedRevisions = append(removedRevisions, rev) + installedRevision = rev + installedBaseURL = baseURL + return tt.installErr + }, + FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { + linkedRevision = rev + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, nil + }, + FuncList: func(_ context.Context) (revs []Revision, err error) { + return slices.Compact([]Revision{ + installedRevision, + tt.cfg.Status.Active, + NewRevision("unknown-version", 0), + }), nil + }, + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevisions = append(removedRevisions, rev) + return nil + }, + FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { + return slices.Contains(tt.linkedRevisions, rev), nil + }, + } + updater.TeleportProcess = &testProcess{ + FuncName: func() string { + return "teleport" + }, + FuncReload: func(_ context.Context) error { + teleportReloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return true, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil + }, + } + updater.TbotProcess = &testProcess{ + FuncName: func() string { + return "tbot" + }, + FuncReload: func(_ context.Context) error { + tbotReloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return true, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil + }, + } + var restarted bool + updater.ReexecSetup = func(_ context.Context, path string, rev Revision, _, reload, _ bool) error { + restarted = reload + setupCalls++ + return tt.setupErr + } + updater.SetupNamespace = func(_ context.Context, path string, rev Revision, _ bool) error { + revertSetupCalls++ return nil - }, - FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { - return slices.Contains(tt.linkedRevisions, rev), nil - }, - } - updater.Process = &testProcess{ - FuncReload: func(_ context.Context) error { - reloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return true, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil - }, - } - var restarted bool - updater.ReexecSetup = func(_ context.Context, path string, rev Revision, _ bool, reload bool) error { - restarted = reload - setupCalls++ - return tt.setupErr - } - updater.SetupNamespace = func(_ context.Context, path string, rev Revision, _ bool) error { - revertSetupCalls++ - return nil - } - - ctx := context.Background() - err = updater.Update(ctx, tt.now) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedBaseURL, installedBaseURL) - require.Equal(t, tt.linkedRevision, linkedRevision) - require.Equal(t, tt.removedRevisions, removedRevisions) - require.Equal(t, tt.flags, installedRevision.Flags) - require.Equal(t, tt.requestGroup, requestedGroup) - require.Equal(t, tt.reloadCalls, reloadCalls) - require.Equal(t, tt.revertCalls, revertSetupCalls) - require.Equal(t, tt.revertCalls, revertFuncCalls) - require.Equal(t, tt.setupCalls, setupCalls) - require.Equal(t, tt.restarted, restarted) + } + updater.HasCustomTbot = func(ctx context.Context) (bool, error) { + return !tbot, nil + } - if tt.cfg == nil { - _, err := os.Stat(cfgPath) - require.Error(t, err) - return - } + ctx := context.Background() + err = updater.Update(ctx, tt.now) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.installedRevision, installedRevision) + require.Equal(t, tt.installedBaseURL, installedBaseURL) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevisions, removedRevisions) + require.Equal(t, tt.flags, installedRevision.Flags) + require.Equal(t, tt.requestGroup, requestedGroup) + require.Equal(t, tt.reloadCalls, teleportReloadCalls) + if tbot && tt.reloadErr == nil { + require.Equal(t, tt.reloadCalls, tbotReloadCalls) + } else { + require.Equal(t, 0, tbotReloadCalls) + } + require.Equal(t, tt.revertCalls, revertSetupCalls) + require.Equal(t, tt.revertCalls, revertFuncCalls) + require.Equal(t, tt.setupCalls, setupCalls) + require.Equal(t, tt.restarted, restarted) + + if tt.cfg == nil { + _, err := os.Stat(cfgPath) + require.Error(t, err) + return + } - data, err := os.ReadFile(cfgPath) - require.NoError(t, err) - data = blankTestAddr(data) + data, err := os.ReadFile(cfgPath) + require.NoError(t, err) + data = blankTestAddr(data) - if golden.ShouldSet() { - golden.Set(t, data) - } - require.Equal(t, string(golden.Get(t)), string(data)) - }) + if golden.ShouldSet() { + golden.Set(t, data) + } + require.Equal(t, string(golden.Get(t)), string(data)) + }) + } } } @@ -1002,7 +1034,7 @@ func TestUpdater_LinkPackage(t *testing.T) { tryLinkSystemCalls: 1, syncCalls: 1, notPresent: true, - errMatch: "cannot find systemd service", + errMatch: "missing Teleport service", }, } @@ -1035,7 +1067,10 @@ func TestUpdater_LinkPackage(t *testing.T) { }, } var syncCalls int - updater.Process = &testProcess{ + updater.TeleportProcess = &testProcess{ + FuncName: func() string { + return "test" + }, FuncSync: func(_ context.Context) error { syncCalls++ return tt.syncErr @@ -1243,6 +1278,23 @@ func TestUpdater_Remove(t *testing.T) { reloadCalls: 1, teardownCalls: 1, }, + { + name: "active version with tbot", + cfg: &UpdateConfig{ + Version: updateConfigVersion, + Kind: updateConfigKind, + Spec: UpdateSpec{ + Path: defaultPathDir, + }, + Status: UpdateStatus{ + Active: NewRevision(version, 0), + }, + }, + linkSystemCalls: 1, + syncCalls: 1, + reloadCalls: 1, + teardownCalls: 1, + }, { name: "active version, no systemd", cfg: &UpdateConfig{ @@ -1278,7 +1330,6 @@ func TestUpdater_Remove(t *testing.T) { syncCalls: 1, reloadCalls: 1, teardownCalls: 1, - reloadErr: ErrNotNeeded, }, { name: "active version, sync error", @@ -1320,84 +1371,114 @@ func TestUpdater_Remove(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - dir := t.TempDir() - ns := &Namespace{installDir: dir} - _, err := ns.Init() - require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) - - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) - updater.TeleportServiceName = serviceName - if tt.serviceName != "" { - updater.TeleportServiceName = tt.serviceName - } - - // Create config file only if provided in test case - if tt.cfg != nil { - b, err := yaml.Marshal(tt.cfg) + for _, tbot := range []bool{false, true} { + t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { + dir := t.TempDir() + ns := &Namespace{installDir: dir} + _, err := ns.Init() require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) + + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) require.NoError(t, err) - } - var ( - linkSystemCalls int - revertFuncCalls int - syncCalls int - reloadCalls int - teardownCalls int - unlinkedVersion string - ) - updater.Installer = &testInstaller{ - FuncLinkSystem: func(_ context.Context) (revert func(context.Context) bool, err error) { - linkSystemCalls++ - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, tt.linkSystemErr - }, - FuncUnlink: func(_ context.Context, rev Revision, path string) error { - unlinkedVersion = rev.Version + // Create config file only if provided in test case + if tt.cfg != nil { + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } + + var ( + linkSystemCalls int + revertFuncCalls int + teleportSyncCalls int + teleportReloadCalls int + tbotSyncCalls int + tbotReloadCalls int + teardownCalls int + unlinkedVersion string + ) + updater.Installer = &testInstaller{ + FuncLinkSystem: func(_ context.Context) (revert func(context.Context) bool, err error) { + linkSystemCalls++ + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, tt.linkSystemErr + }, + FuncUnlink: func(_ context.Context, rev Revision, path string) error { + unlinkedVersion = rev.Version + return nil + }, + } + updater.TeleportProcess = &testProcess{ + FuncName: func() string { + if tt.serviceName != "" { + return tt.serviceName + } + return teleportServiceName + }, + FuncSync: func(_ context.Context) error { + teleportSyncCalls++ + return tt.syncErr + }, + FuncReload: func(_ context.Context) error { + teleportReloadCalls++ + return tt.reloadErr + }, + FuncIsActive: func(_ context.Context) (bool, error) { + return tt.processActive, tt.isActiveErr + }, + } + updater.TbotProcess = &testProcess{ + FuncSync: func(_ context.Context) error { + tbotSyncCalls++ + return tt.syncErr + }, + FuncReload: func(_ context.Context) error { + tbotReloadCalls++ + return tt.reloadErr + }, + FuncIsActive: func(_ context.Context) (bool, error) { + return tt.processActive, tt.isActiveErr + }, + } + updater.TeardownNamespace = func(_ context.Context) error { + teardownCalls++ return nil - }, - } - updater.Process = &testProcess{ - FuncSync: func(_ context.Context) error { - syncCalls++ - return tt.syncErr - }, - FuncReload: func(_ context.Context) error { - reloadCalls++ - return tt.reloadErr - }, - FuncIsActive: func(_ context.Context) (bool, error) { - return tt.processActive, tt.isActiveErr - }, - } - updater.TeardownNamespace = func(_ context.Context) error { - teardownCalls++ - return nil - } + } + updater.HasCustomTbot = func(ctx context.Context) (bool, error) { + return !tbot, nil + } + + ctx := context.Background() + err = updater.Remove(ctx, tt.force) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.syncCalls, teleportSyncCalls) + require.Equal(t, tt.reloadCalls, teleportReloadCalls) + require.Equal(t, 0, tbotSyncCalls) + if tbot && tt.reloadErr == nil { + require.Equal(t, tt.reloadCalls, tbotReloadCalls) + } else { + require.Equal(t, 0, tbotReloadCalls) + } + + require.Equal(t, tt.linkSystemCalls, linkSystemCalls) + require.Equal(t, tt.revertFuncCalls, revertFuncCalls) + require.Equal(t, tt.unlinkedVersion, unlinkedVersion) + require.Equal(t, tt.teardownCalls, teardownCalls) + }) + } - ctx := context.Background() - err = updater.Remove(ctx, tt.force) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.syncCalls, syncCalls) - require.Equal(t, tt.reloadCalls, reloadCalls) - require.Equal(t, tt.linkSystemCalls, linkSystemCalls) - require.Equal(t, tt.revertFuncCalls, revertFuncCalls) - require.Equal(t, tt.unlinkedVersion, unlinkedVersion) - require.Equal(t, tt.teardownCalls, teardownCalls) - }) } } @@ -1668,9 +1749,7 @@ func TestUpdater_Install(t *testing.T) { errMatch: "setup error", }, { - name: "no need to reload", - reloadErr: ErrNotNeeded, - + name: "no need to reload", installedRevision: NewRevision("16.3.0", 0), installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), @@ -1743,161 +1822,194 @@ func TestUpdater_Install(t *testing.T) { } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if len(tt.restrictOS) > 0 && !slices.Contains(tt.restrictOS, runtime.GOOS) { - t.Skip("skipping test because OS is not supported") - } - dir := t.TempDir() - ns := &Namespace{ - installDir: dir, - defaultPathDir: defaultPathDir, - defaultProxyAddr: "default-proxy", - updaterIDFile: "updater-id-file", - } - _, err := ns.Init() - require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) - - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) - - // Create config file only if provided in test case - if tt.cfg != nil { - b, err := yaml.Marshal(tt.cfg) + for _, tbot := range []bool{false, true} { + t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { + if len(tt.restrictOS) > 0 && !slices.Contains(tt.restrictOS, runtime.GOOS) { + t.Skip("skipping test because OS is not supported") + } + dir := t.TempDir() + ns := &Namespace{ + installDir: dir, + defaultPathDir: defaultPathDir, + defaultProxyAddr: "default-proxy", + updaterIDFile: "updater-id-file", + } + _, err := ns.Init() require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) + + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) require.NoError(t, err) - } - var requestedGroup string - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestedGroup = r.URL.Query().Get("group") - config := webclient.PingResponse{ - AutoUpdate: webclient.AutoUpdateSettings{ - AgentVersion: "16.3.0", + // Create config file only if provided in test case + if tt.cfg != nil { + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } + + var requestedGroup string + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestedGroup = r.URL.Query().Get("group") + config := webclient.PingResponse{ + AutoUpdate: webclient.AutoUpdateSettings{ + AgentVersion: "16.3.0", + }, + } + config.Edition = "community" + if tt.flags&autoupdate.FlagEnterprise != 0 { + config.Edition = "ent" + } + if tt.agpl { + config.Edition = "oss" + } + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 + err := json.NewEncoder(w).Encode(config) + require.NoError(t, err) + })) + t.Cleanup(server.Close) + + userCfg := tt.userCfg + if userCfg.Proxy == "" { + userCfg.Proxy = strings.TrimPrefix(server.URL, "https://") + } + updater.DefaultProxyAddr = userCfg.Proxy + + var ( + installedRevision Revision + installedBaseURL string + linkedRevision Revision + removedRevision Revision + revertFuncCalls int + teleportReloadCalls int + tbotReloadCalls int + setupCalls int + revertSetupCalls int + selinuxInstalls int + selinuxRemovals int + ) + updater.Installer = &testInstaller{ + FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { + installedRevision = rev + installedBaseURL = baseURL + return tt.installErr + }, + FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { + linkedRevision = rev + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, nil + }, + FuncList: func(_ context.Context) (revs []Revision, err error) { + return []Revision{}, nil + }, + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevision = rev + return nil + }, + FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { + return false, nil + }, + } + updater.TeleportProcess = &testProcess{ + FuncName: func() string { + return "teleport" + }, + FuncReload: func(_ context.Context) error { + teleportReloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return !tt.notPresent, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil + }, + } + updater.TbotProcess = &testProcess{ + FuncName: func() string { + return "tbot" + }, + FuncReload: func(_ context.Context) error { + tbotReloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return !tt.notPresent, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil }, } - config.Edition = "community" - if tt.flags&autoupdate.FlagEnterprise != 0 { - config.Edition = "ent" + var restarted bool + updater.ReexecSetup = func(_ context.Context, path string, rev Revision, installSELinux, reload, tbot bool) error { + setupCalls++ + if installSELinux { + selinuxInstalls++ + } + restarted = reload + return tt.setupErr } - if tt.agpl { - config.Edition = "oss" + updater.SetupNamespace = func(_ context.Context, path string, rev Revision, installSELinux bool) error { + revertSetupCalls++ + if installSELinux { + selinuxInstalls++ + } else { + selinuxRemovals++ + } + return nil + } + updater.HasCustomTbot = func(ctx context.Context) (bool, error) { + return !tbot, nil } - config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 - err := json.NewEncoder(w).Encode(config) - require.NoError(t, err) - })) - t.Cleanup(server.Close) - if tt.userCfg.Proxy == "" { - tt.userCfg.Proxy = strings.TrimPrefix(server.URL, "https://") - } - updater.DefaultProxyAddr = tt.userCfg.Proxy - - var ( - installedRevision Revision - installedBaseURL string - linkedRevision Revision - removedRevision Revision - revertFuncCalls int - reloadCalls int - setupCalls int - revertSetupCalls int - selinuxInstalls int - selinuxRemovals int - ) - updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { - installedRevision = rev - installedBaseURL = baseURL - return tt.installErr - }, - FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { - linkedRevision = rev - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, nil - }, - FuncList: func(_ context.Context) (revs []Revision, err error) { - return []Revision{}, nil - }, - FuncRemove: func(_ context.Context, rev Revision) error { - removedRevision = rev - return nil - }, - FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { - return false, nil - }, - } - updater.Process = &testProcess{ - FuncReload: func(_ context.Context) error { - reloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return !tt.notPresent, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil - }, - } - var restarted bool - updater.ReexecSetup = func(_ context.Context, path string, rev Revision, installSELinux bool, reload bool) error { - setupCalls++ - if installSELinux { - selinuxInstalls++ + ctx := context.Background() + err = updater.Install(ctx, userCfg) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { + require.NoError(t, err) } - restarted = reload - return tt.setupErr - } - updater.SetupNamespace = func(_ context.Context, path string, rev Revision, installSELinux bool) error { - revertSetupCalls++ - if installSELinux { - selinuxInstalls++ + require.Equal(t, tt.installedRevision, installedRevision) + require.Equal(t, tt.installedBaseURL, installedBaseURL) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevision, removedRevision) + require.Equal(t, tt.flags, installedRevision.Flags) + require.Equal(t, tt.requestGroup, requestedGroup) + require.Equal(t, tt.reloadCalls, teleportReloadCalls) + if tbot && tt.reloadErr == nil { + require.Equal(t, tt.reloadCalls, tbotReloadCalls) } else { - selinuxRemovals++ + require.Equal(t, 0, tbotReloadCalls) } - return nil - } - - ctx := context.Background() - err = updater.Install(ctx, tt.userCfg) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { + require.Equal(t, tt.revertCalls, revertSetupCalls) + require.Equal(t, tt.revertCalls, revertFuncCalls) + require.Equal(t, tt.setupCalls, setupCalls) + require.Equal(t, tt.selinuxInstalls, selinuxInstalls) + require.Equal(t, tt.selinuxRemovals, selinuxRemovals) + require.Equal(t, tt.restarted, restarted) + + data, err := os.ReadFile(cfgPath) require.NoError(t, err) - } - require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedBaseURL, installedBaseURL) - require.Equal(t, tt.linkedRevision, linkedRevision) - require.Equal(t, tt.removedRevision, removedRevision) - require.Equal(t, tt.flags, installedRevision.Flags) - require.Equal(t, tt.requestGroup, requestedGroup) - require.Equal(t, tt.reloadCalls, reloadCalls) - require.Equal(t, tt.revertCalls, revertSetupCalls) - require.Equal(t, tt.revertCalls, revertFuncCalls) - require.Equal(t, tt.setupCalls, setupCalls) - require.Equal(t, tt.selinuxInstalls, selinuxInstalls) - require.Equal(t, tt.selinuxRemovals, selinuxRemovals) - require.Equal(t, tt.restarted, restarted) - - data, err := os.ReadFile(cfgPath) - require.NoError(t, err) - data = blankTestAddr(data) + data = blankTestAddr(data) - if golden.ShouldSet() { - golden.Set(t, data) - } - require.Equal(t, string(golden.Get(t)), string(data)) - }) + if golden.ShouldSet() { + golden.Set(t, data) + } + require.Equal(t, string(golden.Get(t)), string(data)) + }) + } } } @@ -1981,16 +2093,9 @@ func TestUpdater_Setup(t *testing.T) { present: true, }, { - name: "reload not needed", - restart: true, - present: true, - reloadErr: ErrNotNeeded, - }, - { - name: "not present", - restart: true, - present: false, - errMatch: "cannot find systemd", + name: "not present", + restart: true, + present: false, }, { name: "setup error", @@ -2097,7 +2202,22 @@ func TestUpdater_Setup(t *testing.T) { updater, err := NewLocalUpdater(LocalUpdaterConfig{}, ns) require.NoError(t, err) - updater.Process = &testProcess{ + updater.TeleportProcess = &testProcess{ + FuncName: func() string { + return "teleport" + }, + FuncReload: func(_ context.Context) error { + return tt.reloadErr + }, + FuncIsPresent: func(_ context.Context) (bool, error) { + return tt.present, tt.presentErr + }, + } + + updater.TbotProcess = &testProcess{ + FuncName: func() string { + return "tbot" + }, FuncReload: func(_ context.Context) error { return tt.reloadErr }, @@ -2113,7 +2233,15 @@ func TestUpdater_Setup(t *testing.T) { updater.WriteTeleportService = func(_ context.Context, path string, rev Revision) error { require.Equal(t, "test", path) require.Equal(t, "version", rev.Version) - return tt.setupErr + return nil + } + updater.WriteTbotService = func(_ context.Context, path string, rev Revision) error { + require.Equal(t, "test", path) + require.Equal(t, "version", rev.Version) + return nil + } + updater.HasCustomTbot = func(ctx context.Context) (bool, error) { + return false, nil } // Create config file only if provided in test case @@ -2125,7 +2253,7 @@ func TestUpdater_Setup(t *testing.T) { } ctx := context.Background() - err = updater.Setup(ctx, "test", Revision{Version: "version"}, tt.installSELinux, tt.restart) + err = updater.Setup(ctx, "test", Revision{Version: "version"}, tt.installSELinux, tt.restart, true) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -2293,6 +2421,7 @@ func (ti *testInstaller) IsLinked(ctx context.Context, rev Revision, path string } type testProcess struct { + FuncName func() string FuncReload func(ctx context.Context) error FuncSync func(ctx context.Context) error FuncIsEnabled func(ctx context.Context) (bool, error) @@ -2300,6 +2429,10 @@ type testProcess struct { FuncIsPresent func(ctx context.Context) (bool, error) } +func (tp *testProcess) Name() string { + return tp.FuncName() +} + func (tp *testProcess) Reload(ctx context.Context) error { return tp.FuncReload(ctx) } diff --git a/lib/service/service.go b/lib/service/service.go index 95723f14c89af..3043248630ef4 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -59,7 +59,6 @@ import ( "golang.org/x/crypto/acme" "golang.org/x/crypto/acme/autocert" "golang.org/x/crypto/ssh" - "golang.org/x/sys/unix" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" @@ -1646,7 +1645,7 @@ func NewTeleport(cfg *servicecfg.Config) (_ *TeleportProcess, err error) { // create the new pid file only after started successfully if cfg.PIDFile != "" { - if err := createLockedPIDFile(cfg.PIDFile); err != nil { + if err := utils.CreateLockedPIDFile(cfg.PIDFile); err != nil { return nil, trace.Wrap(err, "creating pidfile") } } @@ -7378,35 +7377,3 @@ func (process *TeleportProcess) newExternalAuditStorageConfigurator() (*external statusService := local.NewStatusService(process.backend) return externalauditstorage.NewConfigurator(process.ExitContext(), easSvc, integrationSvc, statusService) } - -// createLockedPIDFile creates a PID file in the path specified by pidFile -// containing the current PID, atomically swapping it in the final place and -// leaving it with an exclusive advisory lock that will get released when the -// process ends, for the benefit of "pkill -L". -func createLockedPIDFile(pidFile string) error { - pending, err := renameio.NewPendingFile(pidFile, renameio.WithPermissions(0o644)) - if err != nil { - return trace.ConvertSystemError(err) - } - defer pending.Cleanup() - if _, err := fmt.Fprintf(pending, "%v\n", os.Getpid()); err != nil { - return trace.ConvertSystemError(err) - } - - const minimumDupFD = 3 // skip stdio - locker, err := unix.FcntlInt(pending.Fd(), unix.F_DUPFD_CLOEXEC, minimumDupFD) - runtime.KeepAlive(pending) - if err != nil { - return trace.ConvertSystemError(err) - } - if err := unix.Flock(locker, unix.LOCK_EX|unix.LOCK_NB); err != nil { - _ = unix.Close(locker) - return trace.ConvertSystemError(err) - } - // deliberately leak the fd to hold the lock until the process dies - - if err := pending.CloseAtomicallyReplace(); err != nil { - return trace.ConvertSystemError(err) - } - return nil -} diff --git a/lib/tbot/cli/start_legacy.go b/lib/tbot/cli/start_legacy.go index b48a698138466..03322e784a9c7 100644 --- a/lib/tbot/cli/start_legacy.go +++ b/lib/tbot/cli/start_legacy.go @@ -132,6 +132,13 @@ type LegacyCommand struct { // If not set, no diagnostics listener is created. DiagAddr string + // DiagSocketForUpdater specifies the diagnostics http service address that + // should be exposed to the updater via UNIX domain socket. + DiagSocketForUpdater string + + // PIDFile is the path to the PID file. If not set, no PID file will be created. + PIDFile string + oneshotSetByUser bool } @@ -158,6 +165,8 @@ func NewLegacyCommand(parentCmd *kingpin.CmdClause, action MutatorAction, mode C c.cmd.Flag("join-method", "Method to use to join the cluster. "+joinMethodList).EnumVar(&c.JoinMethod, onboarding.SupportedJoinMethods...) c.cmd.Flag("oneshot", "If set, quit after the first renewal.").IsSetByUser(&c.oneshotSetByUser).BoolVar(&c.Oneshot) c.cmd.Flag("diag-addr", "If set and the bot is in debug mode, a diagnostics service will listen on specified address.").StringVar(&c.DiagAddr) + c.cmd.Flag("diag-socket-for-updater", "If set, run the diagnostics service on the specified socket path for teleport-update to consume.").Hidden().StringVar(&c.DiagSocketForUpdater) + c.cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created").StringVar(&c.PIDFile) return c } @@ -273,5 +282,8 @@ func (c *LegacyCommand) ApplyConfig(cfg *config.BotConfig, l *slog.Logger) error cfg.DiagAddr = c.DiagAddr } + cfg.DiagSocketForUpdater = c.DiagSocketForUpdater + cfg.PIDFile = c.PIDFile + return nil } diff --git a/lib/tbot/config/config.go b/lib/tbot/config/config.go index 3e1ad26b04568..e230a1ce5ae57 100644 --- a/lib/tbot/config/config.go +++ b/lib/tbot/config/config.go @@ -99,6 +99,13 @@ type BotConfig struct { // If not set, no diagnostics listener is created. DiagAddr string `yaml:"diag_addr,omitempty"` + // DiagSocketForUpdater specifies the path to the diagnostics http service socket that + // should be exposed to the updater. + DiagSocketForUpdater string `yaml:"-"` + + // PIDFile is the path to the PID file that should be created by the bot. + PIDFile string `yaml:"-"` + // ReloadCh allows a channel to be injected into the bot to trigger a // renewal. ReloadCh <-chan struct{} `yaml:"-"` diff --git a/tool/tbot/systemd.tmpl b/lib/tbot/config/systemd/systemd.tmpl similarity index 71% rename from tool/tbot/systemd.tmpl rename to lib/tbot/config/systemd/systemd.tmpl index c058c0fa72e93..a14ec1b11d028 100644 --- a/tool/tbot/systemd.tmpl +++ b/lib/tbot/config/systemd/systemd.tmpl @@ -10,7 +10,7 @@ Group={{ .Group }} Restart=always RestartSec=5 Environment="TELEPORT_ANONYMOUS_TELEMETRY={{ if .AnonymousTelemetry }}1{{ else }}0{{ end }}" -ExecStart={{ .TBotPath }} start -c {{ .ConfigPath }} +ExecStart={{ .TBotPath }} start -c {{ .ConfigPath }}{{ with .DiagSocketForUpdater }} --diag-socket-for-updater={{ . }}{{ end }} --pid-file=/run/{{ .UnitName }}.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/{{ .UnitName }}.pid LimitNOFILE=524288 diff --git a/lib/tbot/config/systemd/template.go b/lib/tbot/config/systemd/template.go new file mode 100644 index 0000000000000..a7539c2eaf338 --- /dev/null +++ b/lib/tbot/config/systemd/template.go @@ -0,0 +1,40 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package systemd + +import ( + _ "embed" + "text/template" +) + +var ( + //go:embed systemd.tmpl + templateData string + Template = template.Must(template.New("").Parse(templateData)) +) + +type TemplateParams struct { + UnitName string + User string + Group string + AnonymousTelemetry bool + ConfigPath string + TBotPath string + DiagSocketForUpdater string +} diff --git a/lib/tbot/internal/diagnostics/service.go b/lib/tbot/internal/diagnostics/service.go index 39a46a5e1c137..90bfe06226349 100644 --- a/lib/tbot/internal/diagnostics/service.go +++ b/lib/tbot/internal/diagnostics/service.go @@ -22,8 +22,10 @@ import ( "context" "errors" "log/slog" + "net" "net/http" "net/http/pprof" + "os" "github.com/gravitational/trace" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -45,6 +47,7 @@ func ServiceBuilder(cfg Config) bot.ServiceBuilder { // Config contains configuration for the diagnostics service. type Config struct { Address string + Network string PProfEnabled bool Logger *slog.Logger } @@ -53,6 +56,9 @@ func (cfg *Config) CheckAndSetDefaults() error { if cfg.Address == "" { return trace.BadParameter("Address is required") } + if cfg.Network == "" { + cfg.Network = "tcp" + } if cfg.Logger == nil { cfg.Logger = slog.Default() } @@ -70,6 +76,7 @@ func NewService(cfg Config, registry readyz.ReadOnlyRegistry) (*Service, error) return &Service{ log: cfg.Logger, diagAddr: cfg.Address, + diagNetwork: cfg.Network, pprofEnabled: cfg.PProfEnabled, statusRegistry: registry, }, nil @@ -80,6 +87,7 @@ func NewService(cfg Config, registry readyz.ReadOnlyRegistry) (*Service, error) type Service struct { log *slog.Logger diagAddr string + diagNetwork string pprofEnabled bool statusRegistry readyz.ReadOnlyRegistry } @@ -131,7 +139,30 @@ func (s *Service) Run(ctx context.Context) error { } }() - if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { + // When the process exists, the socket files are left behind (to cover + // forking scenarios). To guarantee there won't be errors like "address + // already in use", delete the file before starting the listener. + if s.diagNetwork == "unix" { + s.log.DebugContext(ctx, "Deleting socket file", "path", s.diagAddr) + if err := trace.ConvertSystemError(os.Remove(s.diagAddr)); err != nil && !trace.IsNotFound(err) { + s.log.WarnContext(ctx, "Failed to cleanup existing socket file", "error", err) + } + } + + listener, err := net.Listen(s.diagNetwork, s.diagAddr) + if err != nil { + return trace.Wrap(err) + } + + // The default behavior for unix listeners is to delete the file when the + // listener closes (unlinking). However, if the process forks, the file + // descriptor will be gone when its parent process exists, causing the new + // listener to have no socket file. + if unixListener, ok := listener.(*net.UnixListener); ok { + unixListener.SetUnlinkOnClose(false) + } + + if err := srv.Serve(listener); !errors.Is(err, http.ErrServerClosed) { return err } diff --git a/lib/tbot/readyz/readyz.go b/lib/tbot/readyz/readyz.go index 71881c1dfd3f8..e3df4861ce846 100644 --- a/lib/tbot/readyz/readyz.go +++ b/lib/tbot/readyz/readyz.go @@ -19,6 +19,7 @@ package readyz import ( + "os" "sync" "time" @@ -116,6 +117,7 @@ func (r *Registry) OverallStatus() *OverallStatus { return &OverallStatus{ Status: status, + PID: os.Getpid(), Services: services, } } @@ -172,6 +174,9 @@ type OverallStatus struct { // will be Unhealthy. Status Status `json:"status"` + // PID is the process PID. + PID int `json:"pid"` + // Services contains the service-specific statuses. Services map[string]*ServiceStatus `json:"services"` } diff --git a/lib/tbot/readyz/readyz_test.go b/lib/tbot/readyz/readyz_test.go index 4a43a0fec9126..50d7d8807766f 100644 --- a/lib/tbot/readyz/readyz_test.go +++ b/lib/tbot/readyz/readyz_test.go @@ -22,6 +22,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "os" "testing" "time" @@ -64,6 +65,7 @@ func TestReadyz(t *testing.T) { "a": {Status: readyz.Initializing}, "b": {Status: readyz.Initializing}, }, + PID: os.Getpid(), }, response, ) @@ -113,6 +115,7 @@ func TestReadyz(t *testing.T) { "a": {Status: readyz.Healthy, UpdatedAt: &now}, "b": {Status: readyz.Unhealthy, Reason: "database is down", UpdatedAt: &now}, }, + PID: os.Getpid(), }, response, ) @@ -139,6 +142,7 @@ func TestReadyz(t *testing.T) { "a": {Status: readyz.Healthy, UpdatedAt: &now}, "b": {Status: readyz.Healthy, UpdatedAt: &now}, }, + PID: os.Getpid(), }, response, ) diff --git a/lib/tbot/tbot.go b/lib/tbot/tbot.go index ba5287b59eb4e..ea9378cda794b 100644 --- a/lib/tbot/tbot.go +++ b/lib/tbot/tbot.go @@ -170,6 +170,26 @@ func (b *Bot) Run(ctx context.Context) (err error) { ) } + if b.cfg.DiagSocketForUpdater != "" { + services = append(services, + diagnostics.ServiceBuilder(diagnostics.Config{ + Address: b.cfg.DiagSocketForUpdater, + Network: "unix", + Logger: b.log.With( + teleport.ComponentKey, + teleport.Component(teleport.ComponentTBot, "diagnostics-updater"), + ), + }), + ) + } + + // create the new pid file only after started successfully + if b.cfg.PIDFile != "" { + if err := utils.CreateLockedPIDFile(b.cfg.PIDFile); err != nil { + return trace.Wrap(err, "creating pidfile") + } + } + // This faux service allows us to get the bot's internal identity and client // for tests, without exposing them on the core bot.Bot struct. if b.cfg.Testing { diff --git a/lib/utils/pid.go b/lib/utils/pid.go new file mode 100644 index 0000000000000..0c721f14c146b --- /dev/null +++ b/lib/utils/pid.go @@ -0,0 +1,61 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package utils + +import ( + "fmt" + "os" + "runtime" + + "github.com/google/renameio/v2" + "github.com/gravitational/trace" + "golang.org/x/sys/unix" +) + +// CreateLockedPIDFile creates a PID file in the path specified by pidFile +// containing the current PID, atomically swapping it in the final place and +// leaving it with an exclusive advisory lock that will get released when the +// process ends, for the benefit of "pkill -L". +func CreateLockedPIDFile(pidFile string) error { + pending, err := renameio.NewPendingFile(pidFile, renameio.WithPermissions(0o644)) + if err != nil { + return trace.ConvertSystemError(err) + } + defer pending.Cleanup() + if _, err := fmt.Fprintf(pending, "%v\n", os.Getpid()); err != nil { + return trace.ConvertSystemError(err) + } + + const minimumDupFD = 3 // skip stdio + locker, err := unix.FcntlInt(pending.Fd(), unix.F_DUPFD_CLOEXEC, minimumDupFD) + runtime.KeepAlive(pending) + if err != nil { + return trace.ConvertSystemError(err) + } + if err := unix.Flock(locker, unix.LOCK_EX|unix.LOCK_NB); err != nil { + _ = unix.Close(locker) + return trace.ConvertSystemError(err) + } + // deliberately leak the fd to hold the lock until the process dies + + if err := pending.CloseAtomicallyReplace(); err != nil { + return trace.ConvertSystemError(err) + } + return nil +} diff --git a/tool/tbot/systemd.go b/tool/tbot/systemd.go index c61943fce4077..398adc5a311a5 100644 --- a/tool/tbot/systemd.go +++ b/tool/tbot/systemd.go @@ -21,19 +21,18 @@ package main import ( "bytes" "context" - _ "embed" "errors" "fmt" "io" "log/slog" "os" "path/filepath" - "text/template" "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" autoupdate "github.com/gravitational/teleport/lib/autoupdate/agent" + "github.com/gravitational/teleport/lib/tbot/config/systemd" ) type onInstallSystemdCmdFunc func( @@ -83,21 +82,6 @@ func setupInstallSystemdCmd(rootCmd *kingpin.Application) ( return installSystemdCmd.FullCommand(), f } -var ( - //go:embed systemd.tmpl - systemdTemplateData string - systemdTemplate = template.Must(template.New("").Parse(systemdTemplateData)) -) - -type systemdTemplateParams struct { - UnitName string - User string - Group string - AnonymousTelemetry bool - ConfigPath string - TBotPath string -} - func onInstallSystemdCmd( ctx context.Context, log *slog.Logger, @@ -132,7 +116,7 @@ func onInstallSystemdCmd( } buf := bytes.NewBuffer(nil) - err = systemdTemplate.Execute(buf, systemdTemplateParams{ + err = systemd.Template.Execute(buf, systemd.TemplateParams{ UnitName: unitName, User: user, Group: group, diff --git a/tool/tbot/testdata/TestInstallSystemdCmd/succeeds_prexisting_with_force.golden b/tool/tbot/testdata/TestInstallSystemdCmd/succeeds_prexisting_with_force.golden index dc50b23f18226..29e9b490717f6 100644 --- a/tool/tbot/testdata/TestInstallSystemdCmd/succeeds_prexisting_with_force.golden +++ b/tool/tbot/testdata/TestInstallSystemdCmd/succeeds_prexisting_with_force.golden @@ -10,7 +10,7 @@ Group=llamas Restart=always RestartSec=5 Environment="TELEPORT_ANONYMOUS_TELEMETRY=0" -ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml +ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --pid-file=/run/tbot.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/tbot.pid LimitNOFILE=524288 diff --git a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults.golden b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults.golden index e9f947e416a2b..9977e54582fa2 100644 --- a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults.golden +++ b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults.golden @@ -10,7 +10,7 @@ Group=teleport Restart=always RestartSec=5 Environment="TELEPORT_ANONYMOUS_TELEMETRY=0" -ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml +ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --pid-file=/run/tbot.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/tbot.pid LimitNOFILE=524288 diff --git a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults_and_dry_run.golden b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults_and_dry_run.golden index c4f72e6725054..a84900b5fb61f 100644 --- a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults_and_dry_run.golden +++ b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_defaults_and_dry_run.golden @@ -13,7 +13,7 @@ Group=teleport Restart=always RestartSec=5 Environment="TELEPORT_ANONYMOUS_TELEMETRY=0" -ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml +ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --pid-file=/run/tbot.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/tbot.pid LimitNOFILE=524288 diff --git a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_overrides.golden b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_overrides.golden index 3fe862913ac87..b49a62e5b2d38 100644 --- a/tool/tbot/testdata/TestInstallSystemdCmd/success_-_overrides.golden +++ b/tool/tbot/testdata/TestInstallSystemdCmd/success_-_overrides.golden @@ -10,7 +10,7 @@ Group=llamas Restart=always RestartSec=5 Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" -ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml +ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --pid-file=/run/my-farm-bot.pid ExecReload=/bin/kill -HUP $MAINPID PIDFile=/run/my-farm-bot.pid LimitNOFILE=524288 diff --git a/tool/teleport-update/main.go b/tool/teleport-update/main.go index f974b675c3675..0afcc8cb84c5e 100644 --- a/tool/teleport-update/main.go +++ b/tool/teleport-update/main.go @@ -98,6 +98,8 @@ type cliConfig struct { Insecure bool // StatusWithExitCode makes the status command return different exit codes depending on the update status. StatusWithExitCode bool + // SetupTbot specifies whether tbot should be managed. + SetupTbot bool } func Run(args []string) int { @@ -129,7 +131,7 @@ func Run(args []string) int { Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) enableCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) - enableCmd.Flag("overwrite", "Allow existing installed Teleport binaries to be overwritten."). + enableCmd.Flag("overwrite", "Allow existing installed binaries and services to be overwritten."). Short('o').BoolVar(&ccfg.AllowOverwrite) enableCmd.Flag("allow-proxy-conflict", "Allow proxy addresses in teleport.yaml and update.yaml to conflict."). BoolVar(&ccfg.AllowProxyConflict) @@ -153,7 +155,7 @@ func Run(args []string) int { Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) pinCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) - pinCmd.Flag("overwrite", "Allow existing installed Teleport binaries to be overwritten."). + pinCmd.Flag("overwrite", "Allow existing installed binaries and services to be overwritten."). Short('o').BoolVar(&ccfg.AllowOverwrite) pinCmd.Flag("allow-proxy-conflict", "Allow proxy addresses in teleport.yaml and update.yaml to conflict."). BoolVar(&ccfg.AllowProxyConflict) @@ -193,6 +195,8 @@ func Run(args []string) int { Envar(autoupdate.SetupFlagsEnvVar).StringsVar(&ccfg.ForceFlags) setupCmd.Flag("selinux-ssh", "Install the SELinux module for Teleport SSH."). Hidden().Envar(autoupdate.SetupSELinuxSSHEnvVar).BoolVar(&ccfg.SELinuxSSH) + setupCmd.Flag("tbot", "Setup a systemd service for tbot."). + Envar(autoupdate.SetupTbotEnvVar).BoolVar(&ccfg.SetupTbot) statusCmd := app.Command("status", "Show Teleport agent auto-update status.") statusCmd.Flag("err-if-should-update-now", @@ -488,7 +492,7 @@ func cmdSetup(ctx context.Context, ccfg *cliConfig) error { } flags := common.NewInstallFlagsFromStrings(ccfg.ForceFlags) rev := autoupdate.NewRevision(ccfg.ForceVersion, flags) - err = updater.Setup(ctx, ccfg.Path, rev, ccfg.SELinuxSSH, ccfg.Reload) + err = updater.Setup(ctx, ccfg.Path, rev, ccfg.SELinuxSSH, ccfg.Reload, ccfg.SetupTbot) if err != nil { return trace.Wrap(err) } From b7eef79f2f2c86c1d620cdb6b70ae8779b3d4f10 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Wed, 22 Oct 2025 13:40:24 -0400 Subject: [PATCH 02/16] remove teleport-update changes --- lib/autoupdate/agent/installer.go | 9 +- lib/autoupdate/agent/installer_test.go | 26 +- lib/autoupdate/agent/internal/unversioned.go | 33 - .../agent/internal/unversioned_test.go | 190 ---- lib/autoupdate/agent/process.go | 101 +- lib/autoupdate/agent/setup.go | 319 +++---- lib/autoupdate/agent/setup_test.go | 384 +++----- ...olden => FIPS_and_Enterprise_flags.golden} | 0 ...FIPS_and_Enterprise_flags_tbot=true.golden | 12 - ...e.golden => agpl_requires_base_URL.golden} | 0 ...backup_version_kept_for_validation.golden} | 0 ...rsion_kept_for_validation_tbot=true.golden | 13 - ... backup_version_removed_on_install.golden} | 0 ...se.golden => config_does_not_exist.golden} | 0 ...t=false.golden => config_from_file.golden} | 0 .../config_from_file_tbot=true.golden | 15 - ...t=false.golden => config_from_user.golden} | 0 .../config_from_user_tbot=true.golden | 15 - ...stall_tbot=true.golden => defaults.golden} | 0 .../defaults_tbot=true.golden | 13 - ..._tbot=false.golden => insecure_URL.golden} | 0 .../insecure_URL_tbot=true.golden | 11 - ..._tbot=true.golden => install_error.golden} | 0 .../install_error_tbot=true.golden | 10 - ...olden => install_selinux_from_file.golden} | 0 ...olden => install_selinux_from_user.golden} | 0 ...nstall_selinux_from_user_tbot=false.golden | 14 - ...install_selinux_from_user_tbot=true.golden | 14 - ...t=false.golden => invalid_metadata.golden} | 0 .../invalid_metadata_tbot=true.golden | 10 - ...t=true.golden => no_need_to_reload.golden} | 0 ...e.golden => not_started_or_enabled.golden} | 0 .../not_started_or_enabled_tbot=false.golden | 11 - .../not_started_or_enabled_tbot=true.golden | 11 - ...tbot=false.golden => override_skip.golden} | 0 .../override_skip_tbot=false.golden | 13 - .../override_skip_tbot=true.golden | 13 - ...r_tbot=false.golden => setup_fails.golden} | 0 ...n => setup_fails_already_installed.golden} | 0 ...p_fails_already_installed_tbot=true.golden | 10 - .../setup_fails_tbot=false.golden | 10 - .../setup_fails_tbot=true.golden | 10 - ...olden => version_already_installed.golden} | 0 ...ersion_already_installed_tbot=false.golden | 11 - ...version_already_installed_tbot=true.golden | 11 - ...olden => FIPS_and_Enterprise_flags.golden} | 0 ...FIPS_and_Enterprise_flags_tbot=true.golden | 22 - ...e.golden => agpl_requires_base_URL.golden} | 0 .../agpl_requires_base_URL_tbot=true.golden | 18 - ...golden => backup_version_is_linked.golden} | 0 ...backup_version_kept_when_no_change.golden} | 0 ...rsion_kept_when_no_change_tbot=true.golden | 13 - ... backup_version_removed_on_install.golden} | 0 ...ersion_removed_on_install_tbot=true.golden | 19 - ..._tbot=false.golden => insecure_URL.golden} | 0 .../insecure_URL_tbot=true.golden | 11 - ...tbot=false.golden => install_error.golden} | 0 .../install_error_tbot=true.golden | 16 - ...t=false.golden => invalid_metadata.golden} | 0 .../invalid_metadata_tbot=true.golden | 10 - ...lden => missing_path_during_window.golden} | 0 ...issing_path_during_window_tbot=true.golden | 12 - ...bot=false.golden => pinned_version.golden} | 0 .../pinned_version_tbot=true.golden | 13 - ...s_tbot=false.golden => setup_fails.golden} | 0 .../setup_fails_tbot=true.golden | 21 - ..._tbot=false.golden => skip_version.golden} | 0 .../skip_version_tbot=true.golden | 15 - ... => updates_disabled_during_window.golden} | 0 ...updates_disabled_outside_of_window.golden} | 0 ...sabled_outside_of_window_tbot=false.golden | 12 - ...isabled_outside_of_window_tbot=true.golden | 12 - ...n => updates_enabled_during_window.golden} | 0 ...nabled_now,_not_started_or_enabled.golden} | 0 ...w,_not_started_or_enabled_tbot=true.golden | 20 - ...alse.golden => updates_enabled_now.golden} | 0 .../updates_enabled_now_tbot=false.golden | 20 - .../updates_enabled_now_tbot=true.golden | 20 - ... updates_enabled_outside_of_window.golden} | 0 ...enabled_outside_of_window_tbot=true.golden | 12 - ...ersion_already_installed_in_window.golden} | 0 ...lready_installed_outside_of_window.golden} | 0 ...talled_outside_of_window_tbot=false.golden | 11 - ...stalled_outside_of_window_tbot=true.golden | 11 - ...olden => version_detects_as_linked.golden} | 0 ...ersion_detects_as_linked_tbot=false.golden | 19 - ...version_detects_as_linked_tbot=true.golden | 19 - .../no_namespace/needrestart.golden | 1 - .../test_namespace/needrestart.golden | 1 - .../deprecated.golden | 6 - .../dropin.golden | 6 - .../needrestart.golden | 1 - .../service.golden | 9 - .../timer.golden | 13 - .../test_with_custom_tbot/deprecated.golden | 6 - .../test_with_custom_tbot/dropin.golden | 6 - .../test_with_custom_tbot/needrestart.golden | 1 - .../test_with_custom_tbot/service.golden | 9 - .../test_with_custom_tbot/timer.golden | 13 - .../TestWriteTbotService/custom.golden | 22 - .../TestWriteTbotService/custom_suffix.golden | 22 - .../TestWriteTbotService/default.golden | 22 - lib/autoupdate/agent/updater.go | 325 ++----- lib/autoupdate/agent/updater_test.go | 883 ++++++++---------- tool/teleport-update/main.go | 10 +- 105 files changed, 715 insertions(+), 2256 deletions(-) delete mode 100644 lib/autoupdate/agent/internal/unversioned.go delete mode 100644 lib/autoupdate/agent/internal/unversioned_test.go rename lib/autoupdate/agent/testdata/TestUpdater_Install/{FIPS_and_Enterprise_flags_tbot=false.golden => FIPS_and_Enterprise_flags.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{agpl_requires_base_URL_tbot=false.golden => agpl_requires_base_URL.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{backup_version_kept_for_validation_tbot=false.golden => backup_version_kept_for_validation.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{backup_version_removed_on_install_tbot=false.golden => backup_version_removed_on_install.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_does_not_exist_tbot=false.golden => config_does_not_exist.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_from_file_tbot=false.golden => config_from_file.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_from_user_tbot=false.golden => config_from_user.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{backup_version_removed_on_install_tbot=true.golden => defaults.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{insecure_URL_tbot=false.golden => insecure_URL.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{agpl_requires_base_URL_tbot=true.golden => install_error.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_selinux_from_file_tbot=false.golden => install_selinux_from_file.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_selinux_from_file_tbot=true.golden => install_selinux_from_user.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{invalid_metadata_tbot=false.golden => invalid_metadata.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{config_does_not_exist_tbot=true.golden => no_need_to_reload.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{no_need_to_reload_tbot=false.golden => not_started_or_enabled.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{defaults_tbot=false.golden => override_skip.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{install_error_tbot=false.golden => setup_fails.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Install/{setup_fails_already_installed_tbot=false.golden => setup_fails_already_installed.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Install/{no_need_to_reload_tbot=true.golden => version_already_installed.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{FIPS_and_Enterprise_flags_tbot=false.golden => FIPS_and_Enterprise_flags.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{agpl_requires_base_URL_tbot=false.golden => agpl_requires_base_URL.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_is_linked_tbot=false.golden => backup_version_is_linked.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_kept_when_no_change_tbot=false.golden => backup_version_kept_when_no_change.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_is_linked_tbot=true.golden => backup_version_removed_on_install.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{insecure_URL_tbot=false.golden => insecure_URL.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{install_error_tbot=false.golden => install_error.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{invalid_metadata_tbot=false.golden => invalid_metadata.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{missing_path_during_window_tbot=false.golden => missing_path_during_window.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{pinned_version_tbot=false.golden => pinned_version.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{setup_fails_tbot=false.golden => setup_fails.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{skip_version_tbot=false.golden => skip_version.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_disabled_during_window_tbot=false.golden => updates_disabled_during_window.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_disabled_during_window_tbot=true.golden => updates_disabled_outside_of_window.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_during_window_tbot=false.golden => updates_enabled_during_window.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_during_window_tbot=true.golden => updates_enabled_now,_not_started_or_enabled.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_now,_not_started_or_enabled_tbot=false.golden => updates_enabled_now.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{updates_enabled_outside_of_window_tbot=false.golden => updates_enabled_outside_of_window.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{version_already_installed_in_window_tbot=false.golden => version_already_installed_in_window.golden} (100%) rename lib/autoupdate/agent/testdata/TestUpdater_Update/{version_already_installed_in_window_tbot=true.golden => version_already_installed_outside_of_window.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden rename lib/autoupdate/agent/testdata/TestUpdater_Update/{backup_version_removed_on_install_tbot=false.golden => version_detects_as_linked.golden} (100%) delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden delete mode 100644 lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden delete mode 100644 lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden diff --git a/lib/autoupdate/agent/installer.go b/lib/autoupdate/agent/installer.go index 97239d69f74d5..e5e038fa4f68e 100644 --- a/lib/autoupdate/agent/installer.go +++ b/lib/autoupdate/agent/installer.go @@ -56,8 +56,8 @@ const ( const ( // serviceDir contains the relative path to the Teleport SystemD service dir. serviceDir = "lib/systemd/system" - // teleportServiceName contains the upstream name of the Teleport SystemD service file. - teleportServiceName = "teleport.service" + // serviceName contains the upstream name of the Teleport SystemD service file. + serviceName = "teleport.service" ) // ServiceFile represents a systemd service file for a Teleport binary. @@ -380,7 +380,7 @@ func tgzExtractPaths(ent bool) []utils.ExtractPath { prefix += "-ent" } return []utils.ExtractPath{ - {Src: path.Join(prefix, "examples/systemd/teleport.service"), Dst: filepath.Join(serviceDir, teleportServiceName), DirMode: systemDirMode}, + {Src: path.Join(prefix, "examples/systemd/teleport.service"), Dst: filepath.Join(serviceDir, serviceName), DirMode: systemDirMode}, {Src: path.Join(prefix, "examples"), Skip: true, DirMode: systemDirMode}, {Src: path.Join(prefix, "install"), Skip: true, DirMode: systemDirMode}, {Src: path.Join(prefix, "install-selinux.sh"), Skip: true, DirMode: systemDirMode}, @@ -607,7 +607,7 @@ func (li *LocalInstaller) forceLinks(ctx context.Context, srcBinDir, srcSvcDir, return revert, trace.Wrap(ErrNoBinaries) } - // process systemd service files + // create systemd service files for _, s := range li.TargetServices { orig, err := copyService(s, srcSvcDir, dstBinDir, flags) @@ -853,7 +853,6 @@ func (li *LocalInstaller) tryLinks(ctx context.Context, srcBinDir, srcSvcDir, ds } } - // process systemd service files for _, s := range li.TargetServices { _, err := copyService(s, srcSvcDir, dstBinDir, flags) if err != nil && !errors.Is(err, os.ErrExist) { diff --git a/lib/autoupdate/agent/installer_test.go b/lib/autoupdate/agent/installer_test.go index 956146b61f9c3..52fb7b4c89dd1 100644 --- a/lib/autoupdate/agent/installer_test.go +++ b/lib/autoupdate/agent/installer_test.go @@ -214,7 +214,7 @@ func testTGZ(t *testing.T, version string) (tgz *bytes.Buffer, shasum string) { func TestLocalInstaller_Link(t *testing.T) { t.Parallel() const version = "new-version" - servicePath := filepath.Join(serviceDir, teleportServiceName) + servicePath := filepath.Join(serviceDir, serviceName) tests := []struct { name string @@ -459,8 +459,8 @@ func TestLocalInstaller_Link(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, teleportServiceName), - ExampleName: teleportServiceName, + Path: filepath.Join(linkDir, serviceDir, serviceName), + ExampleName: serviceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -527,7 +527,7 @@ func TestLocalInstaller_Link(t *testing.T) { func TestLocalInstaller_TryLink(t *testing.T) { t.Parallel() const version = "new-version" - servicePath := filepath.Join(serviceDir, teleportServiceName) + servicePath := filepath.Join(serviceDir, serviceName) tests := []struct { name string @@ -719,8 +719,8 @@ func TestLocalInstaller_TryLink(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, teleportServiceName), - ExampleName: teleportServiceName, + Path: filepath.Join(linkDir, serviceDir, serviceName), + ExampleName: serviceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -862,8 +862,8 @@ func TestLocalInstaller_Remove(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, teleportServiceName), - ExampleName: teleportServiceName, + Path: filepath.Join(linkDir, serviceDir, serviceName), + ExampleName: serviceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) }, @@ -889,7 +889,7 @@ func TestLocalInstaller_Remove(t *testing.T) { func TestLocalInstaller_IsLinked(t *testing.T) { t.Parallel() const version = "existing-version" - servicePath := filepath.Join(serviceDir, teleportServiceName) + servicePath := filepath.Join(serviceDir, serviceName) tests := []struct { name string @@ -937,8 +937,8 @@ func TestLocalInstaller_IsLinked(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, teleportServiceName), - ExampleName: teleportServiceName, + Path: filepath.Join(linkDir, serviceDir, serviceName), + ExampleName: serviceName, ExampleFunc: func(b []byte, pathDir string, flags autoupdate.InstallFlags) []byte { return fmt.Appendf(nil, "[service=%s][path=%s][flags=%s]", string(b), pathDir, flags.Strings()) @@ -982,7 +982,7 @@ func TestLocalInstaller_IsLinked(t *testing.T) { func TestLocalInstaller_Unlink(t *testing.T) { t.Parallel() const version = "existing-version" - servicePath := filepath.Join(serviceDir, teleportServiceName) + servicePath := filepath.Join(serviceDir, serviceName) tests := []struct { name string @@ -1103,7 +1103,7 @@ func TestLocalInstaller_Unlink(t *testing.T) { InstallDir: versionsDir, TargetServices: []ServiceFile{ { - Path: filepath.Join(linkDir, serviceDir, teleportServiceName), + Path: filepath.Join(linkDir, serviceDir, serviceName), Binary: "teleport", }, }, diff --git a/lib/autoupdate/agent/internal/unversioned.go b/lib/autoupdate/agent/internal/unversioned.go deleted file mode 100644 index 4cb002861acc8..0000000000000 --- a/lib/autoupdate/agent/internal/unversioned.go +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Teleport - * Copyright (C) 2025 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package internal - -// UnversionedTeleport is used to read all versions of teleport.yaml, including -// versions that may now be unsupported. -type UnversionedTeleport struct { - Teleport UnversionedConfig `yaml:"teleport"` -} - -// UnversionedConfig is used to read unversioned configuration from teleport and tbot. -type UnversionedConfig struct { - AuthServers []string `yaml:"auth_servers"` - AuthServer string `yaml:"auth_server"` - ProxyServer string `yaml:"proxy_server"` - DataDir string `yaml:"data_dir"` -} diff --git a/lib/autoupdate/agent/internal/unversioned_test.go b/lib/autoupdate/agent/internal/unversioned_test.go deleted file mode 100644 index 696de1040f3c4..0000000000000 --- a/lib/autoupdate/agent/internal/unversioned_test.go +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Teleport - * Copyright (C) 2025 Gravitational, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package internal_test - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" - - "github.com/gravitational/teleport/lib/autoupdate/agent/internal" - "github.com/gravitational/teleport/lib/config" - tbotconfig "github.com/gravitational/teleport/lib/tbot/config" -) - -// In the future, the latest version of the updater may need to read a version of teleport.yaml that has -// an unsupported version which is supported by the updater-managed version of Teleport. -// This test will break if Teleport removes a field that the updater reads. -func TestUnversionedTeleportConfig(t *testing.T) { - for _, tt := range []struct { - name string - version string - in internal.UnversionedTeleport - err bool - }{ - { - name: "empty", - in: internal.UnversionedTeleport{ - Teleport: internal.UnversionedConfig{ - ProxyServer: "proxy.example.com", - AuthServer: "auth.example.com", - AuthServers: []string{"auth1.example.com", "auth2.example.com"}, - DataDir: "example_dir", - }, - }, - }, - { - name: "v1", - version: string(tbotconfig.V1), - in: internal.UnversionedTeleport{ - Teleport: internal.UnversionedConfig{ - ProxyServer: "proxy.example.com", - AuthServer: "auth.example.com", - AuthServers: []string{"auth1.example.com", "auth2.example.com"}, - DataDir: "example_dir", - }, - }, - }, - { - name: "v2", - version: string(tbotconfig.V2), - in: internal.UnversionedTeleport{ - Teleport: internal.UnversionedConfig{ - ProxyServer: "proxy.example.com", - AuthServer: "auth.example.com", - AuthServers: []string{"auth1.example.com", "auth2.example.com"}, - DataDir: "example_dir", - }, - }, - }, - { - name: "v3", // if this fails, add any new fields to the unversioned config - version: "v3", - in: internal.UnversionedTeleport{ - Teleport: internal.UnversionedConfig{ - ProxyServer: "proxy.example.com", - AuthServer: "auth.example.com", - AuthServers: []string{"auth1.example.com", "auth2.example.com"}, - DataDir: "example_dir", - }, - }, - }, - } { - t.Run(tt.name, func(t *testing.T) { - in := struct { - Version string `yaml:"version"` - internal.UnversionedTeleport `yaml:",inline"` - }{ - Version: tt.version, - UnversionedTeleport: tt.in, - } - var inB bytes.Buffer - err := yaml.NewEncoder(&inB).Encode(in) - require.NoError(t, err) - fc, err := config.ReadConfig(&inB) - if tt.err { - require.Error(t, err) - return - } - require.NoError(t, err) - - var outB bytes.Buffer - err = yaml.NewEncoder(&outB).Encode(fc) - require.NoError(t, err) - - var out internal.UnversionedTeleport - err = yaml.NewDecoder(&outB).Decode(&out) - require.NoError(t, err) - require.Equal(t, tt.in, out) - }) - } - -} - -// In the future, the latest version of the updater may need to read a version of tbot.yaml that has -// an unsupported version which is supported by the updater-managed version of tbot. -// This test will break if tbot removes a field that the updater reads. -func TestUnversionedTbotConfig(t *testing.T) { - for _, tt := range []struct { - name string - version string - in internal.UnversionedConfig - err bool - }{ - { - name: "empty", - in: internal.UnversionedConfig{ - AuthServer: "auth.example.com", - }, - err: true, - }, - { - name: "v1", - version: string(tbotconfig.V1), - in: internal.UnversionedConfig{ - AuthServer: "auth.example.com", - }, - err: true, - }, - { - name: "v2", - version: string(tbotconfig.V2), - in: internal.UnversionedConfig{ - AuthServer: "auth.example.com", - ProxyServer: "proxy.example.com", - }, - }, - { - name: "v3", // when this fails, add any new fields to the unversioned config - version: "v3", - in: internal.UnversionedConfig{}, - err: true, - }, - } { - t.Run(tt.name, func(t *testing.T) { - in := struct { - Version string `yaml:"version"` - internal.UnversionedConfig `yaml:",inline"` - }{ - Version: tt.version, - UnversionedConfig: tt.in, - } - var inB bytes.Buffer - err := yaml.NewEncoder(&inB).Encode(in) - require.NoError(t, err) - fc, err := tbotconfig.ReadConfig(bytes.NewReader(inB.Bytes()), false) - if tt.err { - require.Error(t, err) - return - } - require.NoError(t, err) - - var outB bytes.Buffer - err = yaml.NewEncoder(&outB).Encode(fc) - require.NoError(t, err) - - var out internal.UnversionedConfig - err = yaml.NewDecoder(&outB).Decode(&out) - require.NoError(t, err) - require.Equal(t, tt.in, out) - }) - } -} diff --git a/lib/autoupdate/agent/process.go b/lib/autoupdate/agent/process.go index e537a4040b688..cd76c04cd3e4f 100644 --- a/lib/autoupdate/agent/process.go +++ b/lib/autoupdate/agent/process.go @@ -66,8 +66,6 @@ type SystemdService struct { Ready ReadyChecker // Log contains a logger. Log *slog.Logger - // ForceRestart forces the process to always restart. - ForceRestart bool } // ReadyChecker returns the systemd service readiness status. @@ -75,11 +73,6 @@ type ReadyChecker interface { GetReadiness(ctx context.Context) (debug.Readiness, error) } -// Name of the systemd service. -func (s SystemdService) Name() string { - return s.ServiceName -} - // Reload the systemd service. // Attempts a graceful reload before a hard restart. // See Process interface for more details. @@ -93,7 +86,7 @@ func (s SystemdService) Reload(ctx context.Context) error { // Command error codes < 0 indicate that we are unable to run the command. // Errors from s.systemctl are logged along with stderr and stdout (debug only). - // If the service is not running, return nil. + // If the service is not running, return ErrNotNeeded. // Note systemctl reload returns an error if the unit is not active, and // try-reload-or-restart is too recent of an addition for centos7. code := s.systemctl(ctx, slog.LevelDebug, "is-active", "--quiet", s.ServiceName) @@ -102,7 +95,7 @@ func (s SystemdService) Reload(ctx context.Context) error { return trace.Errorf("unable to determine if systemd service is active") case code > 0: s.Log.WarnContext(ctx, "Systemd service not running.", unitKey, s.ServiceName) - return nil + return trace.Wrap(ErrNotNeeded) } // Get initial PID for crash monitoring. @@ -115,28 +108,20 @@ func (s SystemdService) Reload(ctx context.Context) error { } // Attempt graceful reload of running service. - if !s.ForceRestart { - code = s.systemctl(ctx, slog.LevelError, "reload", s.ServiceName) - switch { - case code < 0: - return trace.Errorf("unable to reload systemd service") - case code > 0: - // Graceful reload fails, try hard restart. - code = s.systemctl(ctx, slog.LevelError, "try-restart", s.ServiceName) - if code != 0 { - return trace.Errorf("hard restart of systemd service failed") - } - s.Log.WarnContext(ctx, "Service ungracefully restarted. Connections potentially dropped.", unitKey, s.ServiceName) - default: - s.Log.InfoContext(ctx, "Gracefully reloaded.", unitKey, s.ServiceName) - } - } else { + code = s.systemctl(ctx, slog.LevelError, "reload", s.ServiceName) + switch { + case code < 0: + return trace.Errorf("unable to reload systemd service") + case code > 0: + // Graceful reload fails, try hard restart. code = s.systemctl(ctx, slog.LevelError, "try-restart", s.ServiceName) if code != 0 { return trace.Errorf("hard restart of systemd service failed") } + s.Log.WarnContext(ctx, "Service ungracefully restarted. Connections potentially dropped.", unitKey, s.ServiceName) + default: + s.Log.InfoContext(ctx, "Gracefully reloaded.", unitKey, s.ServiceName) } - // monitor logs all relevant errors, so we filter for a few outcomes err = s.monitor(ctx, initPID) if errors.Is(err, context.DeadlineExceeded) || @@ -316,9 +301,6 @@ func tickFile(ctx context.Context, path string, ch chan<- int, tickC <-chan time // waitForReady polls the SocketPath unix domain socket with HTTP requests. // If one request returns 200 before the timeout, the service is considered ready. func (s SystemdService) waitForReady(ctx context.Context, pid int, tickC <-chan time.Time) error { - if s.Ready == nil { - return nil - } var lastErr error var readiness debug.Readiness for { @@ -549,67 +531,6 @@ func (s SystemdService) systemctl(ctx context.Context, errLevel slog.Level, args return code } -// ProcessGroup is a group of other Teleport processes. -type ProcessGroup []Process - -func (p ProcessGroup) Name() string { - return "Teleport services" -} - -// Reload reloads all processes in the process group. -func (p ProcessGroup) Reload(ctx context.Context) error { - // TODO(sclevine): consider reloading in parallel if this is too slow for users - for _, process := range p { - if err := process.Reload(ctx); err != nil { - return trace.Wrap(err, "failed to reload %s", process.Name()) - } - } - return nil -} - -// Sync syncs only the first process in the group, and fails if no processes are present. -// The systemctl daemon-reload command is global, so we only need to sync once. -func (p ProcessGroup) Sync(ctx context.Context) error { - if len(p) == 0 { - return trace.Errorf("no services to sync") - } - return trace.Wrap(p[0].Sync(ctx)) -} - -// IsEnabled returns true if any processes in the group are enabled. -func (p ProcessGroup) IsEnabled(ctx context.Context) (bool, error) { - return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { - return p.IsEnabled(ctx) - }) -} - -// IsPresent returns true if any processes in the group are present. -func (p ProcessGroup) IsPresent(ctx context.Context) (bool, error) { - return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { - return p.IsPresent(ctx) - }) -} - -// IsActive returns true if any processes in the group are active. -func (p ProcessGroup) IsActive(ctx context.Context) (bool, error) { - return p.anyAreTrue(ctx, func(ctx context.Context, p Process) (bool, error) { - return p.IsActive(ctx) - }) -} - -func (p ProcessGroup) anyAreTrue(ctx context.Context, f func(ctx context.Context, p Process) (bool, error)) (bool, error) { - for _, process := range p { - ok, err := f(ctx, process) - if err != nil { - return ok, trace.Wrap(err) - } - if ok { - return true, nil - } - } - return false, nil -} - // localExec runs a command locally, logging any output. type localExec struct { // Dir specifies the working directory of the local command. diff --git a/lib/autoupdate/agent/setup.go b/lib/autoupdate/agent/setup.go index ef2accb9ff4ad..8dfb340c46c96 100644 --- a/lib/autoupdate/agent/setup.go +++ b/lib/autoupdate/agent/setup.go @@ -38,11 +38,9 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/lib/autoupdate" - "github.com/gravitational/teleport/lib/autoupdate/agent/internal" "github.com/gravitational/teleport/lib/config/systemd" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/selinux" - tbotsystemd "github.com/gravitational/teleport/lib/tbot/config/systemd" libutils "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/versioncontrol" ) @@ -67,15 +65,6 @@ const ( deprecatedServiceName = "teleport-upgrade.service" ) -const markerPrefix = "# teleport-update " - -// genMarker generates a systemd config file marker that is the -// first part of the header for systemd service files. -// Each revision of Teleport has a unique marker. -func genMarker(rev Revision) string { - return markerPrefix + rev.Dir() + "\n" -} - // genHeader generates a systemd config file header that starts // with the serviceMarker. func genHeader(rev Revision) string { @@ -83,6 +72,13 @@ func genHeader(rev Revision) string { "# DO NOT EDIT THIS FILE\n" } +// genMarker generates a systemd config file marker that is the +// first part of the header for systemd service files. +// Each revision of Teleport has a unique marker. +func genMarker(rev Revision) string { + return "# teleport-update " + rev.Dir() + "\n" +} + const ( updateServiceTemplate = ` [Unit] @@ -115,21 +111,22 @@ Environment="TELEPORT_UPDATE_INSTALL_DIR={{escape .InstallDir}}" ExecStart= ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." ` - // This configuration sets the default value for needrestart-trigger automatic restarts for teleport.service to disabled. // Users may still choose to enable needrestart for teleport.service when installing packaging interactively (or via dpkg config), // but doing so will result in a hard restart that disconnects the agent whenever any dependent libraries are updated. // Other network services, like openvpn, follow this pattern. // It is possible to configure needrestart to trigger a soft restart (via restart.d script), but given that Teleport subprocesses // can use a wide variety of installed binaries (when executed by the user), this could trigger many unexpected reloads. - needrestartConfTemplate = `{{range .Services}}$nrconf{override_rc}{qr(^{{replace . "." "\\."}})} = 0; -{{end}}` + needrestartConfTemplate = `$nrconf{override_rc}{qr(^{{replace .TeleportService "." "\\."}})} = 0; +` ) -// confParams parameterizes the above updater configuration templates. +// standard Makefile included in 'selinux-policy-devel' RPM package +// used to build SELinux modules +const selinuxMakefile = "/usr/share/selinux/devel/Makefile" + type confParams struct { TeleportService string - Services []string UpdaterBinary string InstallSuffix string InstallDir string @@ -137,10 +134,6 @@ type confParams struct { UpdaterConfigFile string } -// standard Makefile included in 'selinux-policy-devel' RPM package -// used to build SELinux modules -const selinuxMakefile = "/usr/share/selinux/devel/Makefile" - // Namespace represents a namespace within various system paths for a isolated installation of Teleport. type Namespace struct { log *slog.Logger @@ -150,34 +143,28 @@ type Namespace struct { installDir string // defaultPathDir for Teleport binaries (ns: /opt/teleport/myns/bin) defaultPathDir string - // defaultProxyAddr parsed from teleport.yaml, if present - defaultProxyAddr string // dataDir parsed from teleport.yaml, if present dataDir string - // teleportServiceFile for the Teleport systemd service (ns: /etc/systemd/system/teleport_myns.service) - teleportServiceFile string - // teleportConfigFile for Teleport config (ns: /etc/teleport_myns.yaml) - teleportConfigFile string - // teleportPIDFile for Teleport (ns: /run/teleport_myns.pid) - teleportPIDFile string - // teleportDropInFile is the Teleport systemd drop-in path extending Teleport - teleportDropInFile string - // tbotServiceFile is the systemd service path for tbot (ns: /etc/systemd/system/tbot_myns.service) - tbotServiceFile string - // tbotConfigFile for tbot config (ns: /etc/tbot_myns.yaml) - tbotConfigFile string - // tbotPIDFile for tbot (ns: /run/tbot_myns.pid) - tbotPIDFile string + // defaultProxyAddr parsed from teleport.yaml, if present + defaultProxyAddr string + // serviceFile for the Teleport systemd service (ns: /etc/systemd/system/teleport_myns.service) + serviceFile string + // configFile for Teleport config (ns: /etc/teleport_myns.yaml) + configFile string + // pidFile for Teleport (ns: /run/teleport_myns.pid) + pidFile string // updaterIDFile contains the updater's temporary ID file updaterIDFile string // updaterServiceFile is the systemd service path for the updater updaterServiceFile string // updaterTimerFile is the systemd timer path for the updater updaterTimerFile string - // needrestartConfigFile is the path to needrestart configuration for all services - needrestartConfigFile string + // teleportDropInFile is the Teleport systemd drop-in path extending Teleport + teleportDropInFile string // deprecatedDropInFile is the deprecated upgrader's systemd drop-in path deprecatedDropInFile string + // needrestartConfFile is the path to needrestart configuration for Teleport + needrestartConfFile string } var alphanum = regexp.MustCompile("^[a-zA-Z0-9-]*$") @@ -201,45 +188,39 @@ func NewNamespace(ctx context.Context, log *slog.Logger, name, installDir string if name == "" { linkDir := defaultPathDir return &Namespace{ - log: log, - name: name, - installDir: installDir, - defaultPathDir: linkDir, - dataDir: defaults.DataDir, - teleportServiceFile: filepath.Join("/", serviceDir, teleportServiceName), // /lib for backwards-compat - teleportConfigFile: defaults.ConfigFilePath, - teleportPIDFile: filepath.Join(systemdPIDDir, "teleport.pid"), - needrestartConfigFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), - teleportDropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), - updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), - updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), - updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), - deprecatedDropInFile: filepath.Join(systemdAdminDir, deprecatedServiceName+".d", BinaryName+".conf"), - tbotServiceFile: filepath.Join(systemdAdminDir, "tbot.service"), - tbotConfigFile: filepath.Join("/etc", "tbot.yaml"), - tbotPIDFile: filepath.Join(systemdPIDDir, "tbot.pid"), + log: log, + name: name, + installDir: installDir, + defaultPathDir: linkDir, + dataDir: defaults.DataDir, + serviceFile: filepath.Join("/", serviceDir, serviceName), + configFile: defaults.ConfigFilePath, + pidFile: filepath.Join(systemdPIDDir, "teleport.pid"), + updaterIDFile: filepath.Join(os.TempDir(), BinaryName+".id"), + updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+".service"), + updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+".timer"), + teleportDropInFile: filepath.Join(systemdAdminDir, "teleport.service.d", BinaryName+".conf"), + deprecatedDropInFile: filepath.Join(systemdAdminDir, deprecatedServiceName+".d", BinaryName+".conf"), + needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+".conf"), }, nil } prefix := "teleport_" + name linkDir := filepath.Join(installDir, name, "bin") return &Namespace{ - log: log, - name: name, - installDir: installDir, - defaultPathDir: linkDir, - dataDir: filepath.Join(filepath.Dir(defaults.DataDir), prefix), - teleportServiceFile: filepath.Join(systemdAdminDir, prefix+".service"), - teleportConfigFile: filepath.Join(filepath.Dir(defaults.ConfigFilePath), prefix+".yaml"), - teleportPIDFile: filepath.Join(systemdPIDDir, prefix+".pid"), - needrestartConfigFile: filepath.Join(needrestartConfDir, BinaryName+"_"+name+".conf"), - teleportDropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), - updaterIDFile: filepath.Join(os.TempDir(), BinaryName+"_"+name+".id"), - updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".service"), - updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".timer"), - tbotServiceFile: filepath.Join(systemdAdminDir, "tbot_"+name+".service"), - tbotConfigFile: filepath.Join("/etc", "tbot_"+name+".yaml"), - tbotPIDFile: filepath.Join(systemdPIDDir, "tbot_"+name+".pid"), + log: log, + name: name, + installDir: installDir, + defaultPathDir: linkDir, + dataDir: filepath.Join(filepath.Dir(defaults.DataDir), prefix), + serviceFile: filepath.Join(systemdAdminDir, prefix+".service"), + configFile: filepath.Join(filepath.Dir(defaults.ConfigFilePath), prefix+".yaml"), + pidFile: filepath.Join(systemdPIDDir, prefix+".pid"), + updaterIDFile: filepath.Join(os.TempDir(), BinaryName+"_"+name+".id"), + updaterServiceFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".service"), + updaterTimerFile: filepath.Join(systemdAdminDir, BinaryName+"_"+name+".timer"), + teleportDropInFile: filepath.Join(systemdAdminDir, prefix+".service.d", BinaryName+"_"+name+".conf"), + needrestartConfFile: filepath.Join(needrestartConfDir, BinaryName+"_"+name+".conf"), // no deprecatedDropInFile, as teleport-upgrade does not conflict with namespaced installs }, nil } @@ -334,7 +315,7 @@ func (ns *Namespace) installSELinux(ctx context.Context, rev Revision) error { } binaryPath := filepath.Join(ns.Dir(), versionsDirName, rev.Dir(), "bin", "teleport") - fileCtxs, err := selinux.FileContexts(ns.dataDir, ns.teleportConfigFile, binaryPath) + fileCtxs, err := selinux.FileContexts(ns.dataDir, ns.configFile, binaryPath) if err != nil { return trace.Wrap(err) } @@ -397,17 +378,17 @@ func (ns *Namespace) createAndLabelDirs(ctx context.Context, cmd localExec) erro } // Create an empty teleport.yaml config file if it doesn't exist, and // only attempt to label it if it exists. - if libutils.FileExists(ns.teleportConfigFile) { - dirsToLabel = append(dirsToLabel, filepath.Dir(ns.teleportConfigFile)) + if libutils.FileExists(ns.configFile) { + dirsToLabel = append(dirsToLabel, filepath.Dir(ns.configFile)) } else { - confFile, err := os.OpenFile(ns.teleportConfigFile, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0o640) + confFile, err := os.OpenFile(ns.configFile, os.O_RDONLY|os.O_CREATE|os.O_EXCL, 0o640) if err != nil { - ns.log.WarnContext(ctx, "Failed to create teleport.yaml.", errorKey, err, "path", ns.teleportConfigFile) + ns.log.WarnContext(ctx, "Failed to create teleport.yaml.", errorKey, err, "path", ns.configFile) ns.log.WarnContext(ctx, "You will likely need to create teleport.yaml and re-run 'sudo teleport-update enable' for Teleport SSH to work correctly with SELinux.") } else { confFile.Close() - dirsToLabel = append(dirsToLabel, filepath.Dir(ns.teleportConfigFile)) - ns.log.WarnContext(ctx, "Created an empty teleport.yaml.", "path", ns.teleportConfigFile) + dirsToLabel = append(dirsToLabel, filepath.Dir(ns.configFile)) + ns.log.WarnContext(ctx, "Created an empty teleport.yaml.", "path", ns.configFile) ns.log.WarnContext(ctx, "If you move or copy another file onto this file you will likely need to re-run 'sudo teleport-update enable' for Teleport SSH to work correctly with SELinux.") } } @@ -452,7 +433,7 @@ func (ns *Namespace) removeSELinux(ctx context.Context) error { return trace.Wrap(err, "failed to remove module") } - _, err = cmd.Run(ctx, "restorecon", "-rv", filepath.Clean(ns.installDir), ns.dataDir, filepath.Dir(ns.teleportConfigFile)) + _, err = cmd.Run(ctx, "restorecon", "-rv", filepath.Clean(ns.installDir), ns.dataDir, filepath.Dir(ns.configFile)) if err != nil { return trace.Wrap(err, "failed to restore file contexts") } @@ -523,7 +504,7 @@ func (ns *Namespace) Teardown(ctx context.Context) error { ns.updaterTimerFile, ns.teleportDropInFile, ns.deprecatedDropInFile, - ns.needrestartConfigFile, + ns.needrestartConfFile, } { if p == "" { continue @@ -569,8 +550,7 @@ func (ns *Namespace) Teardown(ctx context.Context) error { } func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revision) error { - teleportService := filepath.Base(ns.teleportServiceFile) - tbotService := filepath.Base(ns.tbotServiceFile) + teleportService := filepath.Base(ns.serviceFile) params := confParams{ TeleportService: teleportService, UpdaterBinary: filepath.Join(path, BinaryName), @@ -596,17 +576,8 @@ func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revi return trace.Wrap(err) } } - // Needrestart config is non-critical for updater functionality. - - nrServices := []string{teleportService} - if ok, err := ns.HasCustomTbot(ctx); err != nil { - ns.log.ErrorContext(ctx, "Unable to determine if tbot is managed by the updater, skipping needrestart configuration.", errorKey, err) - } else if !ok { - nrServices = append(nrServices, tbotService) - } - - _, err := os.Stat(filepath.Dir(ns.needrestartConfigFile)) + _, err := os.Stat(filepath.Dir(ns.needrestartConfFile)) if os.IsNotExist(err) { return nil // needrestart is not present } @@ -614,10 +585,8 @@ func (ns *Namespace) writeConfigFiles(ctx context.Context, path string, rev Revi ns.log.ErrorContext(ctx, "Unable to disable needrestart.", errorKey, err) return nil } - ns.log.InfoContext(ctx, "Disabling needrestart.") - err = writeSystemTemplate(ns.needrestartConfigFile, "", needrestartConfTemplate, confParams{ - Services: nrServices, - }) + ns.log.InfoContext(ctx, "Disabling needrestart.", unitKey, teleportService) + err = writeSystemTemplate(ns.needrestartConfFile, "", needrestartConfTemplate, params) if err != nil { ns.log.ErrorContext(ctx, "Unable to disable needrestart.", errorKey, err) return nil @@ -669,72 +638,22 @@ func (ns *Namespace) WriteTeleportService(_ context.Context, pathDir string, rev if pathDir == "" { pathDir = ns.defaultPathDir } - return trace.Wrap(writeAtomicWithinDir(ns.teleportServiceFile, configFileMode, func(w io.Writer) error { + return trace.Wrap(writeAtomicWithinDir(ns.serviceFile, configFileMode, func(w io.Writer) error { _, err := fmt.Fprint(w, genHeader(rev)+"\n") if err != nil { return trace.Wrap(err) } return trace.Wrap(systemd.WriteUnitFile(systemd.Flags{ EnvironmentFile: systemd.DefaultEnvironmentFile, - PIDFile: ns.teleportPIDFile, + PIDFile: ns.pidFile, FileDescriptorLimit: systemd.DefaultFileDescriptorLimit, TeleportInstallationFile: filepath.Join(pathDir, "teleport"), - TeleportConfigPath: ns.teleportConfigFile, + TeleportConfigPath: ns.configFile, FIPS: rev.Flags&autoupdate.FlagFIPS != 0, }, w)) })) } -func (ns *Namespace) HasCustomTbot(ctx context.Context) (bool, error) { - diskMarkerPrefix, err := readFileLimit(ns.tbotServiceFile, int64(len(markerPrefix))) - if errors.Is(err, os.ErrNotExist) { - ns.log.DebugContext(ctx, "Service not present.", "path", ns.tbotServiceFile) - return false, nil - } - if err != nil { - return false, trace.Wrap(err) - } - return string(diskMarkerPrefix) != markerPrefix, nil -} - -// WriteTbotService writes the tbot systemd service for the version of tbot -// that matches the version of tbot compiled into the executing code. -func (ns *Namespace) WriteTbotService(ctx context.Context, pathDir string, rev Revision) error { - if pathDir == "" { - pathDir = ns.defaultPathDir - } - // This configuration preserves the original tbot systemd.tmpl params. - // In the future, the template could be updated to accept PIDFile directly. - unitName := "tbot" - if ns.name != "" { - unitName = "tbot_" + ns.name - } - if ns.tbotPIDFile != filepath.Join("/run", unitName+".pid") { - return trace.Errorf("invalid PID file: %q", ns.tbotPIDFile) - } - diagDir := filepath.Join(ns.dataDir, "bot") - - if err := os.MkdirAll(diagDir, teleport.PrivateDirMode); err != nil { - ns.log.WarnContext(ctx, "Failed to create tbot data directory.", errorKey, err) - } - - return trace.Wrap(writeAtomicWithinDir(ns.tbotServiceFile, configFileMode, func(w io.Writer) error { - _, err := fmt.Fprint(w, genHeader(rev)+"\n") - if err != nil { - return trace.Wrap(err) - } - return trace.Wrap(tbotsystemd.Template.Execute(w, tbotsystemd.TemplateParams{ - UnitName: unitName, - User: "root", - Group: "root", - AnonymousTelemetry: true, - ConfigPath: ns.tbotConfigFile, - TBotPath: filepath.Join(pathDir, "tbot"), - DiagSocketForUpdater: filepath.Join(diagDir, teleport.DebugServiceSocketName), - })) - })) -} - // ReplaceTeleportService replaces the default paths in the Teleport service config with namespaced paths. // This function is still used for backwards-compatibility, but string-replaced systemd services // are always overridden in more recent versions of Teleport. @@ -755,11 +674,11 @@ func (ns *Namespace) ReplaceTeleportService(cfg []byte, path string, flags autou }, { old: "/etc/teleport.yaml", - new: ns.teleportConfigFile, + new: ns.configFile, }, { old: "/run/teleport.pid", - new: ns.teleportPIDFile, + new: ns.pidFile, }, { old: "/teleport start ", @@ -786,76 +705,50 @@ func (ns *Namespace) LogWarnings(ctx context.Context, pathDir string) { if pathDir == "" { pathDir = ns.defaultPathDir } - ns.log.WarnContext(ctx, "Custom install suffix specified.", "suffix", ns.name, "path_dir", pathDir) - ns.log.WarnContext(ctx, "Teleport data_dir must be set in the config file, and paths will be different.", + ns.log.WarnContext(ctx, "Custom install suffix specified. Teleport data_dir must be configured in the config file.", "data_dir", ns.dataDir, - "config_file", ns.teleportConfigFile, - "service", filepath.Base(ns.teleportServiceFile), - "pid_file", ns.teleportPIDFile, - ) - ns.log.WarnContext(ctx, "Tbot data storage must be set in the config file, and paths will be different.", - "data_dir", filepath.Join(ns.dataDir, "bot"), - "config_file", ns.tbotConfigFile, - "service", filepath.Base(ns.tbotServiceFile), - "pid_file", ns.tbotPIDFile, + "path_dir", pathDir, + "config_file", ns.configFile, + "service", filepath.Base(ns.serviceFile), + "pid_file", ns.pidFile, ) } -// overrideFromConfig loads fields from teleport configuration into the namespace, overriding any defaults. +// unversionedConfig is used to read all versions of teleport.yaml, including +// versions that may now be unsupported. +type unversionedConfig struct { + Teleport unversionedTeleport `yaml:"teleport"` +} + +type unversionedTeleport struct { + AuthServers []string `yaml:"auth_servers"` + AuthServer string `yaml:"auth_server"` + ProxyServer string `yaml:"proxy_server"` + DataDir string `yaml:"data_dir"` +} + +// overrideFromConfig loads fields from teleport.yaml into the namespace, overriding any defaults. func (ns *Namespace) overrideFromConfig(ctx context.Context) { - if ns == nil { + if ns == nil || ns.configFile == "" { return } - var ( - configFile string - cfg internal.UnversionedTeleport - ) - - switch { - case ns.teleportConfigFile != "": - // Use values from teleport.yaml if present. - configFile = ns.teleportConfigFile - f, err := libutils.OpenFileAllowingUnsafeLinks(configFile) - if err != nil { - ns.log.DebugContext(ctx, "Unable to open Teleport config to read proxy or data dir", "config_file", configFile, errorKey, err) - return - } - defer f.Close() - if err := yaml.NewDecoder(f).Decode(&cfg); err != nil { - ns.log.DebugContext(ctx, "Unable to parse Teleport config to read proxy or data dir", "config_file", configFile, errorKey, err) - return - } - if cfg.Teleport.DataDir != "" { - ns.dataDir = cfg.Teleport.DataDir - } - case ns.tbotConfigFile != "": - // Otherwise, use values from tbot.yaml (bot-only installation). - // Do not attempt to read tbot's storage dir as the data directory used to store the tbot socket. - // Ignore tbot configuration if the updater does not manage the tbot service. - configFile = ns.tbotConfigFile - customTbot, err := ns.HasCustomTbot(ctx) - if err != nil { - ns.log.DebugContext(ctx, "Unable to determine if tbot is managed by the updater", "config_file", configFile, errorKey, err) - return - } - if customTbot { - return - } - f, err := libutils.OpenFileAllowingUnsafeLinks(configFile) - if err != nil { - ns.log.DebugContext(ctx, "Unable to open tbot config to read proxy or data dir", "config_file", configFile, errorKey, err) - return - } - defer f.Close() - if err := yaml.NewDecoder(f).Decode(&cfg.Teleport); err != nil { - ns.log.DebugContext(ctx, "Unable to parse tbot config to read proxy or data dir", "config_file", configFile, errorKey, err) - return - } - default: + path := ns.configFile + f, err := libutils.OpenFileAllowingUnsafeLinks(path) + if err != nil { + ns.log.DebugContext(ctx, "Unable to open Teleport config to read proxy or data dir", "config", path, errorKey, err) + return + } + defer f.Close() + var cfg unversionedConfig + if err := yaml.NewDecoder(f).Decode(&cfg); err != nil { + ns.log.DebugContext(ctx, "Unable to parse Teleport config to read proxy or data dir", "config", path, errorKey, err) return } + if cfg.Teleport.DataDir != "" { + ns.dataDir = cfg.Teleport.DataDir + } - // Any implicitly defaulted port in configuration is explicitly defaulted (to 3080). + // Any implicitly defaulted port in teleport.yaml is explicitly defaulted (to 3080). var addr string var port int @@ -870,12 +763,12 @@ func (ns *Namespace) overrideFromConfig(ctx context.Context) { addr = t.AuthServers[0] port = defaults.AuthListenPort default: - ns.log.DebugContext(ctx, "Unable to find proxy in config", "config_file", configFile) + ns.log.DebugContext(ctx, "Unable to find proxy in Teleport config", "config", path, errorKey, err) return } netaddr, err := libutils.ParseHostPortAddr(addr, port) if err != nil { - ns.log.DebugContext(ctx, "Unable to parse proxy in config", "config_file", configFile, "proxy_addr", addr, "proxy_port", port, errorKey, err) + ns.log.DebugContext(ctx, "Unable to parse proxy in Teleport config", "config", path, "proxy_addr", addr, "proxy_port", port, errorKey, err) return } ns.defaultProxyAddr = netaddr.String() diff --git a/lib/autoupdate/agent/setup_test.go b/lib/autoupdate/agent/setup_test.go index dc24787db3d23..42311e56fefbd 100644 --- a/lib/autoupdate/agent/setup_test.go +++ b/lib/autoupdate/agent/setup_test.go @@ -31,7 +31,7 @@ import ( "gopkg.in/yaml.v3" "github.com/gravitational/teleport/lib/autoupdate" - "github.com/gravitational/teleport/lib/autoupdate/agent/internal" + "github.com/gravitational/teleport/lib/config" "github.com/gravitational/teleport/lib/utils/testutils/golden" ) @@ -46,63 +46,54 @@ func TestNewNamespace(t *testing.T) { { name: "no namespace", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/opt/teleport", - defaultPathDir: "/usr/local/bin", - teleportServiceFile: "/lib/systemd/system/teleport.service", - teleportConfigFile: "/etc/teleport.yaml", - teleportPIDFile: "/run/teleport.pid", - needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update.conf", - teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", - tbotServiceFile: "/etc/systemd/system/tbot.service", - tbotConfigFile: "/etc/tbot.yaml", - tbotPIDFile: "/run/tbot.pid", + dataDir: "/var/lib/teleport", + installDir: "/opt/teleport", + defaultPathDir: "/usr/local/bin", + serviceFile: "/lib/systemd/system/teleport.service", + configFile: "/etc/teleport.yaml", + pidFile: "/run/teleport.pid", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", }, }, { name: "no namespace with dirs", installDir: "/install", ns: &Namespace{ - dataDir: "/var/lib/teleport", - installDir: "/install", - defaultPathDir: "/usr/local/bin", - teleportServiceFile: "/lib/systemd/system/teleport.service", - teleportConfigFile: "/etc/teleport.yaml", - teleportPIDFile: "/run/teleport.pid", - updaterIDFile: "/TMP/teleport-update.id", - updaterServiceFile: "/etc/systemd/system/teleport-update.service", - updaterTimerFile: "/etc/systemd/system/teleport-update.timer", - teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", - deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", - tbotServiceFile: "/etc/systemd/system/tbot.service", - tbotConfigFile: "/etc/tbot.yaml", - tbotPIDFile: "/run/tbot.pid", - needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update.conf", + dataDir: "/var/lib/teleport", + installDir: "/install", + defaultPathDir: "/usr/local/bin", + serviceFile: "/lib/systemd/system/teleport.service", + configFile: "/etc/teleport.yaml", + pidFile: "/run/teleport.pid", + updaterIDFile: "/TMP/teleport-update.id", + updaterServiceFile: "/etc/systemd/system/teleport-update.service", + updaterTimerFile: "/etc/systemd/system/teleport-update.timer", + teleportDropInFile: "/etc/systemd/system/teleport.service.d/teleport-update.conf", + deprecatedDropInFile: "/etc/systemd/system/teleport-upgrade.service.d/teleport-update.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update.conf", }, }, { name: "test namespace", namespace: "test", ns: &Namespace{ - name: "test", - dataDir: "/var/lib/teleport_test", - installDir: "/opt/teleport", - defaultPathDir: "/opt/teleport/test/bin", - teleportServiceFile: "/etc/systemd/system/teleport_test.service", - teleportConfigFile: "/etc/teleport_test.yaml", - teleportPIDFile: "/run/teleport_test.pid", - updaterIDFile: "/TMP/teleport-update_test.id", - updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", - updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", - tbotServiceFile: "/etc/systemd/system/tbot_test.service", - tbotConfigFile: "/etc/tbot_test.yaml", - tbotPIDFile: "/run/tbot_test.pid", - needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update_test.conf", + name: "test", + dataDir: "/var/lib/teleport_test", + installDir: "/opt/teleport", + defaultPathDir: "/opt/teleport/test/bin", + serviceFile: "/etc/systemd/system/teleport_test.service", + configFile: "/etc/teleport_test.yaml", + pidFile: "/run/teleport_test.pid", + updaterIDFile: "/TMP/teleport-update_test.id", + updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", + updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, { @@ -110,21 +101,18 @@ func TestNewNamespace(t *testing.T) { namespace: "test", installDir: "/install", ns: &Namespace{ - name: "test", - dataDir: "/var/lib/teleport_test", - installDir: "/install", - defaultPathDir: "/install/test/bin", - teleportConfigFile: "/etc/teleport_test.yaml", - teleportPIDFile: "/run/teleport_test.pid", - teleportServiceFile: "/etc/systemd/system/teleport_test.service", - updaterIDFile: "/TMP/teleport-update_test.id", - updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", - updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", - teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", - tbotServiceFile: "/etc/systemd/system/tbot_test.service", - tbotConfigFile: "/etc/tbot_test.yaml", - tbotPIDFile: "/run/tbot_test.pid", - needrestartConfigFile: "/etc/needrestart/conf.d/teleport-update_test.conf", + name: "test", + dataDir: "/var/lib/teleport_test", + installDir: "/install", + defaultPathDir: "/install/test/bin", + configFile: "/etc/teleport_test.yaml", + pidFile: "/run/teleport_test.pid", + serviceFile: "/etc/systemd/system/teleport_test.service", + updaterIDFile: "/TMP/teleport-update_test.id", + updaterServiceFile: "/etc/systemd/system/teleport-update_test.service", + updaterTimerFile: "/etc/systemd/system/teleport-update_test.timer", + teleportDropInFile: "/etc/systemd/system/teleport_test.service.d/teleport-update_test.conf", + needrestartConfFile: "/etc/needrestart/conf.d/teleport-update_test.conf", }, }, { @@ -159,9 +147,8 @@ func TestNewNamespace(t *testing.T) { func TestWriteConfigFiles(t *testing.T) { for _, p := range []struct { - name string - namespace string - customTbot bool + name string + namespace string }{ { name: "no namespace", @@ -170,14 +157,6 @@ func TestWriteConfigFiles(t *testing.T) { name: "test namespace", namespace: "test", }, - { - name: "test with custom tbot", - customTbot: true, - }, - { - name: "test namespace with custom tbot", - customTbot: true, - }, } { t.Run(p.name, func(t *testing.T) { log := slog.Default() @@ -185,18 +164,12 @@ func TestWriteConfigFiles(t *testing.T) { ctx := context.Background() ns, err := NewNamespace(ctx, log, p.namespace, "") require.NoError(t, err) + ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), filepath.Base(ns.updaterServiceFile)) ns.updaterServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterServiceFile) ns.updaterTimerFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.updaterTimerFile) ns.teleportDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.teleportDropInFile))), ns.teleportDropInFile) ns.deprecatedDropInFile = rebasePath(filepath.Join(linkDir, serviceDir, filepath.Base(filepath.Dir(ns.deprecatedDropInFile))), ns.deprecatedDropInFile) - ns.needrestartConfigFile = rebasePath(linkDir, filepath.Base(ns.needrestartConfigFile)) - if p.customTbot { - ns.tbotServiceFile = rebasePath(filepath.Join(linkDir, serviceDir), ns.tbotServiceFile) - err := os.MkdirAll(filepath.Dir(ns.tbotServiceFile), os.ModePerm) - require.NoError(t, err) - err = os.WriteFile(ns.tbotServiceFile, []byte("custom"), os.ModePerm) - require.NoError(t, err) - } + ns.needrestartConfFile = rebasePath(linkDir, filepath.Base(ns.needrestartConfFile)) err = ns.writeConfigFiles(ctx, linkDir, NewRevision("version", 0)) require.NoError(t, err) @@ -208,7 +181,7 @@ func TestWriteConfigFiles(t *testing.T) { {name: "timer", path: ns.updaterTimerFile}, {name: "dropin", path: ns.teleportDropInFile}, {name: "deprecated", path: ns.deprecatedDropInFile}, - {name: "needrestart", path: ns.needrestartConfigFile}, + {name: "needrestart", path: ns.needrestartConfFile}, } { if tt.path == "" { continue @@ -229,52 +202,6 @@ func TestWriteConfigFiles(t *testing.T) { } } -func TestHasCustomTbot(t *testing.T) { - for _, tt := range []struct { - name string - present bool - header bool - - result bool - }{ - { - name: "does not exist", - }, - { - name: "exists", - present: true, - result: true, - }, - { - name: "exists with header", - present: true, - header: true, - }, - } { - t.Run(tt.name, func(t *testing.T) { - tempdir := t.TempDir() - ns := &Namespace{ - log: slog.Default(), - tbotServiceFile: filepath.Join(tempdir, "tbot.service"), - } - err := os.MkdirAll(filepath.Dir(ns.tbotServiceFile), os.ModePerm) - require.NoError(t, err) - header := "custom" - if tt.header { - header = markerPrefix - } - if tt.present { - err = os.WriteFile(ns.tbotServiceFile, []byte(header), os.ModePerm) - require.NoError(t, err) - } - ctx := context.Background() - res, err := ns.HasCustomTbot(ctx) - require.NoError(t, err) - require.Equal(t, tt.result, res) - }) - } -} - func rebasePath(newBase, oldPath string) string { if oldPath == "" { return "" @@ -293,15 +220,13 @@ func TestNamespace_overrideFromConfig(t *testing.T) { t.Parallel() tests := []struct { - name string - teleportConfig *internal.UnversionedConfig - tbotConfig *internal.UnversionedConfig - customTbot bool - want Namespace + name string + cfg *unversionedTeleport + want Namespace }{ { name: "default", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "example.com", DataDir: "/data", }, @@ -311,8 +236,8 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, }, { - name: "empty", - teleportConfig: &internal.UnversionedConfig{}, + name: "empty", + cfg: &unversionedTeleport{}, want: Namespace{ defaultProxyAddr: "default.example.com", dataDir: "/var/lib/teleport", @@ -320,7 +245,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "full proxy", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "https://example.com:8080", }, want: Namespace{ @@ -330,7 +255,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "protocol and host", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "https://example.com", }, want: Namespace{ @@ -340,7 +265,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "host and port", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "example.com:443", }, want: Namespace{ @@ -350,7 +275,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "host", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "example.com", }, want: Namespace{ @@ -360,7 +285,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth server (v3)", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ AuthServer: "example.com", }, want: Namespace{ @@ -370,7 +295,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth server (v1/2)", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ AuthServers: []string{ "one.example.com", "two.example.com", @@ -383,7 +308,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "proxy priority", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ ProxyServer: "one.example.com", AuthServer: "two.example.com", AuthServers: []string{"three.example.com"}, @@ -395,7 +320,7 @@ func TestNamespace_overrideFromConfig(t *testing.T) { }, { name: "auth priority", - teleportConfig: &internal.UnversionedConfig{ + cfg: &unversionedTeleport{ AuthServer: "two.example.com", AuthServers: []string{"three.example.com"}, }, @@ -411,80 +336,58 @@ func TestNamespace_overrideFromConfig(t *testing.T) { dataDir: "/var/lib/teleport", }, }, - { - name: "tbot managed", - tbotConfig: &internal.UnversionedConfig{ - ProxyServer: "example.com", - }, - want: Namespace{ - defaultProxyAddr: "example.com:3080", - dataDir: "/var/lib/teleport", - }, - }, - { - name: "tbot unmanaged", - tbotConfig: &internal.UnversionedConfig{ - ProxyServer: "example.com", - }, - customTbot: true, - want: Namespace{ - defaultProxyAddr: "default.example.com", - dataDir: "/var/lib/teleport", - }, - }, - { - name: "teleport overrides tbot", - teleportConfig: &internal.UnversionedConfig{ - ProxyServer: "example.com", - DataDir: "/data", - }, - tbotConfig: &internal.UnversionedConfig{ - ProxyServer: "other.example.com", - DataDir: "/other-data", - }, - want: Namespace{ - defaultProxyAddr: "example.com:3080", - dataDir: "/data", - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ns := &Namespace{ log: slog.Default(), + configFile: filepath.Join(t.TempDir(), "teleport.yaml"), defaultProxyAddr: "default.example.com", dataDir: "/var/lib/teleport", } - if tt.customTbot { - ns.tbotServiceFile = filepath.Join(t.TempDir(), "tbot.service") - err := os.WriteFile(ns.tbotServiceFile, []byte("custom"), os.ModePerm) - require.NoError(t, err) - } - if tt.teleportConfig != nil { - ns.teleportConfigFile = filepath.Join(t.TempDir(), "teleport.yaml") - out, err := yaml.Marshal(internal.UnversionedTeleport{Teleport: *tt.teleportConfig}) - require.NoError(t, err) - err = os.WriteFile(ns.teleportConfigFile, out, os.ModePerm) - require.NoError(t, err) - } - if tt.tbotConfig != nil { - ns.tbotConfigFile = filepath.Join(t.TempDir(), "tbot.yaml") - out, err := yaml.Marshal(tt.tbotConfig) + if tt.cfg != nil { + out, err := yaml.Marshal(unversionedConfig{Teleport: *tt.cfg}) require.NoError(t, err) - err = os.WriteFile(ns.tbotConfigFile, out, os.ModePerm) + err = os.WriteFile(ns.configFile, out, os.ModePerm) require.NoError(t, err) } ctx := context.Background() ns.overrideFromConfig(ctx) - ns.teleportConfigFile = "" - ns.tbotConfigFile = "" - ns.tbotServiceFile = "" + ns.configFile = "" ns.log = nil require.Equal(t, &tt.want, ns) }) } } +// In the future, the latest version of the updater may need to read a version of teleport.yaml that has +// an unsupported version which is supported by the updater-managed version of Teleport. +// This test will break if Teleport removes a field that the updater reads. +func TestUnversionedTeleportConfig(t *testing.T) { + in := unversionedConfig{ + Teleport: unversionedTeleport{ + ProxyServer: "proxy.example.com", + AuthServer: "auth.example.com", + AuthServers: []string{"auth1.example.com", "auth2.example.com"}, + DataDir: "example_dir", + }, + } + var inB bytes.Buffer + err := yaml.NewEncoder(&inB).Encode(in) + require.NoError(t, err) + fc, err := config.ReadConfig(&inB) + require.NoError(t, err) + + var outB bytes.Buffer + err = yaml.NewEncoder(&outB).Encode(fc) + require.NoError(t, err) + + var out unversionedConfig + err = yaml.NewDecoder(&outB).Decode(&out) + require.NoError(t, err) + require.Equal(t, in, out) +} + func TestWriteTeleportService(t *testing.T) { t.Parallel() @@ -520,10 +423,10 @@ func TestWriteTeleportService(t *testing.T) { t.Run(tt.name, func(t *testing.T) { serviceFile := filepath.Join(t.TempDir(), "file") ns := &Namespace{ - log: slog.Default(), - teleportConfigFile: tt.configFile, - teleportServiceFile: serviceFile, - teleportPIDFile: tt.pidFile, + log: slog.Default(), + configFile: tt.configFile, + serviceFile: serviceFile, + pidFile: tt.pidFile, } err := ns.WriteTeleportService(context.Background(), tt.pathDir, NewRevision("version", tt.flags)) require.NoError(t, err) @@ -537,77 +440,6 @@ func TestWriteTeleportService(t *testing.T) { } } -func TestWriteTbotService(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - - suffix string - pidFile string - configFile string - pathDir string - dataDir string - err bool - }{ - { - name: "default", - pidFile: "/run/tbot.pid", - configFile: "/etc/tbot.yaml", - pathDir: "/usr/local/bin", - dataDir: "/var/lib/teleport", - }, - { - name: "custom", - pidFile: "/run/tbot.pid", - configFile: "/some/path/tbot.yaml", - pathDir: "/some/path/bin", - dataDir: "/some/path", - }, - { - name: "custom suffix", - suffix: "suffix", - pidFile: "/run/tbot_suffix.pid", - configFile: "/some/path/tbot.yaml", - pathDir: "/some/path/bin", - dataDir: "/some/path", - }, - { - name: "bad pid", - pidFile: "/some/path/tbot.pid", - configFile: "/some/path/tbot.yaml", - pathDir: "/some/path/bin", - dataDir: "/some/path", - err: true, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - serviceFile := filepath.Join(t.TempDir(), "file") - ns := &Namespace{ - log: slog.Default(), - name: tt.suffix, - tbotConfigFile: tt.configFile, - tbotServiceFile: serviceFile, - tbotPIDFile: tt.pidFile, - dataDir: tt.dataDir, - } - err := ns.WriteTbotService(context.Background(), tt.pathDir, NewRevision("version", 0)) - if tt.err { - require.Error(t, err) - return - } - require.NoError(t, err) - data, err := os.ReadFile(serviceFile) - require.NoError(t, err) - if golden.ShouldSet() { - golden.Set(t, data) - } - require.Equal(t, string(golden.Get(t)), string(data)) - }) - } -} - func TestReplaceTeleportService(t *testing.T) { t.Parallel() @@ -666,9 +498,9 @@ WantedBy=multi-user.target for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ns := &Namespace{ - log: slog.Default(), - teleportConfigFile: tt.configFile, - teleportPIDFile: tt.pidFile, + log: slog.Default(), + configFile: tt.configFile, + pidFile: tt.pidFile, } data := ns.ReplaceTeleportService([]byte(tt.in), tt.pathDir, tt.flags) if golden.ShouldSet() { diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden deleted file mode 100644 index 0a42425a845c0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/FIPS_and_Enterprise_flags_tbot=true.golden +++ /dev/null @@ -1,12 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden deleted file mode 100644 index d7028f84581d7..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_kept_for_validation_tbot=true.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden deleted file mode 100644 index fb2c1f4c3156e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_file_tbot=true.golden +++ /dev/null @@ -1,15 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /path - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden deleted file mode 100644 index 42a4ebb9cedb4..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_from_user_tbot=true.golden +++ /dev/null @@ -1,15 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /path - group: new-group - base_url: https://example.com/new - enabled: true - pinned: false -status: - id_file: updater-id-file - active: - version: new-version - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/backup_version_removed_on_install_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/defaults.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden deleted file mode 100644 index c2342d86c90e9..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=true.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden deleted file mode 100644 index 69b08b9c83c9d..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/insecure_URL_tbot=true.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: "" - path: "" - base_url: http://example.com - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/agpl_requires_base_URL_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_error.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden deleted file mode 100644 index 6e104086250e3..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=true.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: "" - path: "" - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_file_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden deleted file mode 100644 index 13cbb0b358e62..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=false.golden +++ /dev/null @@ -1,14 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false - selinux_ssh: true -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden deleted file mode 100644 index 13cbb0b358e62..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_selinux_from_user_tbot=true.golden +++ /dev/null @@ -1,14 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false - selinux_ssh: true -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden deleted file mode 100644 index 0c3dcaac8edbd..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/invalid_metadata_tbot=true.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: "" -kind: "" -spec: - proxy: "" - path: "" - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/config_does_not_exist_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden deleted file mode 100644 index e16d92d41752e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=false.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden deleted file mode 100644 index e16d92d41752e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/not_started_or_enabled_tbot=true.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/defaults_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden deleted file mode 100644 index c2342d86c90e9..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=false.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden deleted file mode 100644 index c2342d86c90e9..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/override_skip_tbot=true.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/install_error_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden deleted file mode 100644 index 067fcf60bd527..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_already_installed_tbot=true.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: "" - path: "" - enabled: false - pinned: false -status: - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden deleted file mode 100644 index 6e104086250e3..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=false.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: "" - path: "" - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden deleted file mode 100644 index 6e104086250e3..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/setup_fails_tbot=true.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: "" - path: "" - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Install/no_need_to_reload_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden deleted file mode 100644 index e16d92d41752e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=false.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden deleted file mode 100644 index e16d92d41752e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Install/version_already_installed_tbot=true.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: false - pinned: false -status: - id_file: updater-id-file - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden deleted file mode 100644 index 14a0b83b8539e..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/FIPS_and_Enterprise_flags_tbot=true.golden +++ /dev/null @@ -1,22 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - flags: [Enterprise, FIPS] - active: - version: 16.3.0 - flags: [Enterprise, FIPS] - backup: - version: old-version - flags: [Enterprise, FIPS] diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden deleted file mode 100644 index 667353a7c24ae..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/agpl_requires_base_URL_tbot=true.golden +++ /dev/null @@ -1,18 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: false - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: old-version - backup: - version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden deleted file mode 100644 index d257cd6c30282..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_kept_when_no_change_tbot=true.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: 16.3.0 - backup: - version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_is_linked_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden deleted file mode 100644 index 7697a84f3326c..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=true.golden +++ /dev/null @@ -1,19 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden deleted file mode 100644 index 297b00ce4ecf8..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/insecure_URL_tbot=true.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: http://example.com - enabled: true - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/install_error.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden deleted file mode 100644 index 575463dc75100..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/install_error_tbot=true.golden +++ /dev/null @@ -1,16 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: false - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden deleted file mode 100644 index a771da1fc9e25..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/invalid_metadata_tbot=true.golden +++ /dev/null @@ -1,10 +0,0 @@ -version: "" -kind: "" -spec: - proxy: localhost - path: "" - enabled: false - pinned: false -status: - active: - version: "" diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden deleted file mode 100644 index f29735ca0230b..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/missing_path_during_window_tbot=true.golden +++ /dev/null @@ -1,12 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: "" - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden deleted file mode 100644 index 501506cf96c78..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/pinned_version_tbot=true.golden +++ /dev/null @@ -1,13 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: true -status: - active: - version: old-version - backup: - version: backup-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden deleted file mode 100644 index 170e821a409b2..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/setup_fails_tbot=true.golden +++ /dev/null @@ -1,21 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: false - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: old-version - backup: - version: backup-version - skip: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden deleted file mode 100644 index 10b36430ffed1..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/skip_version_tbot=true.golden +++ /dev/null @@ -1,15 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: old-version - backup: - version: backup-version - skip: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_during_window_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden deleted file mode 100644 index b6b43595a5903..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=false.golden +++ /dev/null @@ -1,12 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: false - pinned: false -status: - active: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden deleted file mode 100644 index b6b43595a5903..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_disabled_outside_of_window_tbot=true.golden +++ /dev/null @@ -1,12 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: false - pinned: false -status: - active: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_during_window_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden deleted file mode 100644 index efdd858023547..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=true.golden +++ /dev/null @@ -1,20 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now,_not_started_or_enabled_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden deleted file mode 100644 index efdd858023547..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=false.golden +++ /dev/null @@ -1,20 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden deleted file mode 100644 index efdd858023547..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_now_tbot=true.golden +++ /dev/null @@ -1,20 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden deleted file mode 100644 index a4cac37b8733c..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/updates_enabled_outside_of_window_tbot=true.golden +++ /dev/null @@ -1,12 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - group: group - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_in_window_tbot=true.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden deleted file mode 100644 index 926667a2e7fc0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=false.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden deleted file mode 100644 index 926667a2e7fc0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_already_installed_outside_of_window_tbot=true.golden +++ /dev/null @@ -1,11 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - active: - version: 16.3.0 diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked.golden similarity index 100% rename from lib/autoupdate/agent/testdata/TestUpdater_Update/backup_version_removed_on_install_tbot=false.golden rename to lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked.golden diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden deleted file mode 100644 index 7697a84f3326c..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=false.golden +++ /dev/null @@ -1,19 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden b/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden deleted file mode 100644 index 7697a84f3326c..0000000000000 --- a/lib/autoupdate/agent/testdata/TestUpdater_Update/version_detects_as_linked_tbot=true.golden +++ /dev/null @@ -1,19 +0,0 @@ -version: v1 -kind: update_config -spec: - proxy: localhost - path: /usr/local/bin - base_url: https://example.com - enabled: true - pinned: false -status: - id_file: updater-id-file - last_update: - success: true - time: 2025-01-01T00:00:00Z - target: - version: 16.3.0 - active: - version: 16.3.0 - backup: - version: old-version diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden index 85b19ed507a35..b5d6a74435cb2 100644 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/no_namespace/needrestart.golden @@ -1,2 +1 @@ $nrconf{override_rc}{qr(^teleport\.service)} = 0; -$nrconf{override_rc}{qr(^tbot\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden index 4994533b2e2cd..ad6bd606a74cb 100644 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden +++ b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace/needrestart.golden @@ -1,2 +1 @@ $nrconf{override_rc}{qr(^teleport_test\.service)} = 0; -$nrconf{override_rc}{qr(^tbot_test\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden deleted file mode 100644 index fcaaae54ce5d0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/deprecated.golden +++ /dev/null @@ -1,6 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Service] -ExecStart= -ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden deleted file mode 100644 index 4ca6b61b76342..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/dropin.golden +++ /dev/null @@ -1,6 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Service] -Environment="TELEPORT_UPDATE_CONFIG_FILE=/opt/teleport/default/update.yaml" -Environment="TELEPORT_UPDATE_INSTALL_DIR=/opt/teleport" diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden deleted file mode 100644 index b5d6a74435cb2..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/needrestart.golden +++ /dev/null @@ -1 +0,0 @@ -$nrconf{override_rc}{qr(^teleport\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden deleted file mode 100644 index e3038b3255fc0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/service.golden +++ /dev/null @@ -1,9 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Unit] -Description=Teleport auto-update service - -[Service] -Type=oneshot -ExecStart=/usr/local/bin/teleport-update --install-suffix= "--install-dir=/opt/teleport" update diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden deleted file mode 100644 index df921a1e883f1..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_namespace_with_custom_tbot/timer.golden +++ /dev/null @@ -1,13 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Unit] -Description=Teleport auto-update timer unit - -[Timer] -OnActiveSec=1m -OnUnitActiveSec=5m -RandomizedDelaySec=1m - -[Install] -WantedBy=teleport.service diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden deleted file mode 100644 index fcaaae54ce5d0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/deprecated.golden +++ /dev/null @@ -1,6 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Service] -ExecStart= -ExecStart=-/bin/echo "The teleport-upgrade script has been disabled by teleport-update. Please remove the teleport-ent-updater package." diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden deleted file mode 100644 index 4ca6b61b76342..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/dropin.golden +++ /dev/null @@ -1,6 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Service] -Environment="TELEPORT_UPDATE_CONFIG_FILE=/opt/teleport/default/update.yaml" -Environment="TELEPORT_UPDATE_INSTALL_DIR=/opt/teleport" diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden deleted file mode 100644 index b5d6a74435cb2..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/needrestart.golden +++ /dev/null @@ -1 +0,0 @@ -$nrconf{override_rc}{qr(^teleport\.service)} = 0; diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden deleted file mode 100644 index e3038b3255fc0..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/service.golden +++ /dev/null @@ -1,9 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Unit] -Description=Teleport auto-update service - -[Service] -Type=oneshot -ExecStart=/usr/local/bin/teleport-update --install-suffix= "--install-dir=/opt/teleport" update diff --git a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden b/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden deleted file mode 100644 index df921a1e883f1..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteConfigFiles/test_with_custom_tbot/timer.golden +++ /dev/null @@ -1,13 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -[Unit] -Description=Teleport auto-update timer unit - -[Timer] -OnActiveSec=1m -OnUnitActiveSec=5m -RandomizedDelaySec=1m - -[Install] -WantedBy=teleport.service diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden deleted file mode 100644 index cff076375ac53..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom.golden +++ /dev/null @@ -1,22 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -# Generated by `tbot` -[Unit] -Description=tbot - Teleport Machine ID Service -After=network.target - -[Service] -Type=simple -User=root -Group=root -Restart=always -RestartSec=5 -Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" -ExecStart=/some/path/bin/tbot start -c /some/path/tbot.yaml --diag-socket-for-updater=/some/path/bot/debug.sock --pid-file=/run/tbot.pid -ExecReload=/bin/kill -HUP $MAINPID -PIDFile=/run/tbot.pid -LimitNOFILE=524288 - -[Install] -WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden deleted file mode 100644 index 37334d016ae23..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteTbotService/custom_suffix.golden +++ /dev/null @@ -1,22 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -# Generated by `tbot` -[Unit] -Description=tbot_suffix - Teleport Machine ID Service -After=network.target - -[Service] -Type=simple -User=root -Group=root -Restart=always -RestartSec=5 -Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" -ExecStart=/some/path/bin/tbot start -c /some/path/tbot.yaml --diag-socket-for-updater=/some/path/bot/debug.sock --pid-file=/run/tbot_suffix.pid -ExecReload=/bin/kill -HUP $MAINPID -PIDFile=/run/tbot_suffix.pid -LimitNOFILE=524288 - -[Install] -WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden b/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden deleted file mode 100644 index c269618ba3484..0000000000000 --- a/lib/autoupdate/agent/testdata/TestWriteTbotService/default.golden +++ /dev/null @@ -1,22 +0,0 @@ -# teleport-update version -# DO NOT EDIT THIS FILE - -# Generated by `tbot` -[Unit] -Description=tbot - Teleport Machine ID Service -After=network.target - -[Service] -Type=simple -User=root -Group=root -Restart=always -RestartSec=5 -Environment="TELEPORT_ANONYMOUS_TELEMETRY=1" -ExecStart=/usr/local/bin/tbot start -c /etc/tbot.yaml --diag-socket-for-updater=/var/lib/teleport/bot/debug.sock --pid-file=/run/tbot.pid -ExecReload=/bin/kill -HUP $MAINPID -PIDFile=/run/tbot.pid -LimitNOFILE=524288 - -[Install] -WantedBy=multi-user.target diff --git a/lib/autoupdate/agent/updater.go b/lib/autoupdate/agent/updater.go index 25eb6e8e92836..cb06d96b94d6c 100644 --- a/lib/autoupdate/agent/updater.go +++ b/lib/autoupdate/agent/updater.go @@ -59,8 +59,6 @@ const ( SetupVersionEnvVar = "TELEPORT_UPDATE_SETUP_VERSION" // SetupFlagsEnvVar specifies Teleport version flags. SetupFlagsEnvVar = "TELEPORT_UPDATE_SETUP_FLAGS" - // SetupTbotEnvVar specifies that the updater should manage tbot. - SetupTbotEnvVar = "TELEPORT_UPDATE_SETUP_TBOT" // SetupSELinuxSSHEnvVar is the environment variable that enables SELinux SSH support. SetupSELinuxSSHEnvVar = "TELEPORT_UPDATE_SELINUX_SSH" ) @@ -128,35 +126,27 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { cfg.SystemDir = packageSystemDir } validator := Validator{Log: cfg.Log} - teleportDebugClient := debug.NewClient(ns.dataDir) - // Teleport's debug client happens to work for tbot, but this may not - // be intentional. In the future, we might consider extracting a generic - // debug client that can be used in both contexts. - tbotDebugClient := debug.NewClient(filepath.Join(ns.dataDir, "bot")) - + debugClient := debug.NewClient(ns.dataDir) return &Updater{ - Log: cfg.Log, - Pool: certPool, - InsecureSkipVerify: cfg.InsecureSkipVerify, - UpdateConfigFile: filepath.Join(ns.Dir(), updateConfigName), - UpdateIDFile: ns.updaterIDFile, - MachineIDFile: systemdMachineIDFile, - TeleportIDFile: filepath.Join(ns.dataDir, teleportHostIDFileName), - TeleportConfigFile: ns.teleportConfigFile, - DefaultProxyAddr: ns.defaultProxyAddr, - DefaultPathDir: ns.defaultPathDir, + Log: cfg.Log, + Pool: certPool, + InsecureSkipVerify: cfg.InsecureSkipVerify, + UpdateConfigFile: filepath.Join(ns.Dir(), updateConfigName), + UpdateIDFile: ns.updaterIDFile, + MachineIDFile: systemdMachineIDFile, + TeleportIDFile: filepath.Join(ns.dataDir, teleportHostIDFileName), + TeleportConfigFile: ns.configFile, + TeleportServiceName: filepath.Base(ns.serviceFile), + DefaultProxyAddr: ns.defaultProxyAddr, + DefaultPathDir: ns.defaultPathDir, Installer: &LocalInstaller{ InstallDir: filepath.Join(ns.Dir(), versionsDirName), TargetServices: []ServiceFile{ { - Path: ns.teleportServiceFile, + Path: ns.serviceFile, Binary: "teleport", - ExampleName: teleportServiceName, - ExampleFunc: ns.ReplaceTeleportService, // required for TryLinkSystem - }, - { - Path: ns.tbotServiceFile, - Binary: "tbot", + ExampleName: serviceName, + ExampleFunc: ns.ReplaceTeleportService, }, }, SystemBinDir: filepath.Join(cfg.SystemDir, "bin"), @@ -168,23 +158,14 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { ValidateBinary: validator.IsBinary, Template: autoupdate.DefaultCDNURITemplate, }, - TeleportProcess: &SystemdService{ - ServiceName: filepath.Base(ns.teleportServiceFile), - PIDFile: ns.teleportPIDFile, - Ready: teleportDebugClient, + Process: &SystemdService{ + ServiceName: filepath.Base(ns.serviceFile), + PIDFile: ns.pidFile, + Ready: debugClient, Log: cfg.Log, }, - TbotProcess: &SystemdService{ - ServiceName: filepath.Base(ns.tbotServiceFile), - PIDFile: ns.tbotPIDFile, - Ready: tbotDebugClient, - Log: cfg.Log, - ForceRestart: true, - }, WriteTeleportService: ns.WriteTeleportService, - WriteTbotService: ns.WriteTbotService, - HasCustomTbot: ns.HasCustomTbot, - ReexecSetup: func(ctx context.Context, pathDir string, rev Revision, enableSELinux, reload, tbot bool) error { + ReexecSetup: func(ctx context.Context, pathDir string, rev Revision, enableSELinux, reload bool) error { name := filepath.Join(pathDir, BinaryName) if cfg.SelfSetup && runtime.GOOS == constants.LinuxOS { name = "/proc/self/exe" @@ -206,13 +187,11 @@ func NewLocalUpdater(cfg LocalUpdaterConfig, ns *Namespace) (*Updater, error) { cmd := exec.CommandContext(ctx, name, args...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout - // Never set env vars conditionally, as they may be set in the parent environment. cmd.Env = append(slices.Clone(os.Environ()), SetupVersionEnvVar+"="+rev.Version, SetupFlagsEnvVar+"="+strings.Join(rev.Flags.Strings(), "\n"), - SetupSELinuxSSHEnvVar+"="+strconv.FormatBool(enableSELinux), - SetupTbotEnvVar+"="+strconv.FormatBool(tbot), ) + cmd.Env = append(cmd.Env, SetupSELinuxSSHEnvVar+"="+strconv.FormatBool(enableSELinux)) cfg.Log.InfoContext(ctx, "Executing new teleport-update binary to update configuration.") defer cfg.Log.InfoContext(ctx, "Finished executing new teleport-update binary.") return trace.Wrap(cmd.Run()) @@ -263,28 +242,23 @@ type Updater struct { TeleportIDFile string // TeleportConfigFile contains the path to Teleport's configuration. TeleportConfigFile string + // TeleportServiceName contains the full name of the systemd service for Teleport + TeleportServiceName string // DefaultProxyAddr contains Teleport's proxy address. This may differ from the updater's. DefaultProxyAddr string // DefaultPathDir contains the default path that Teleport binaries should be installed into. DefaultPathDir string // Installer manages installations of the Teleport agent. Installer Installer - // TeleportProcess manages a running instance of Teleport. - TeleportProcess Process - // TbotProcess manages a running instance of tbot. - TbotProcess Process + // Process manages a running instance of Teleport. + Process Process // WriteTeleportService writes the teleport systemd service for the version of Teleport // matching the currently running updater. WriteTeleportService func(ctx context.Context, path string, rev Revision) error - // WriteTbotService writes the tbot systemd service for the version of Teleport - // matching the currently running updater. - WriteTbotService func(ctx context.Context, path string, rev Revision) error - // HasCustomTbot returns true if a non-updater-managed tbot installation is present. - HasCustomTbot func(ctx context.Context) (bool, error) // ReexecSetup re-execs teleport-update with the setup command. // This configures an SELinux module, configures the updater service, // verifies the installation, and optionally reloads Teleport. - ReexecSetup func(ctx context.Context, path string, rev Revision, installSELinux, reload, tbot bool) error + ReexecSetup func(ctx context.Context, path string, rev Revision, installSELinux, reload bool) error // SetupNamespace configures the Teleport updater service for the current Namespace // and configures an SELinux module. SetupNamespace func(ctx context.Context, path string, rev Revision, installSELinux bool) error @@ -335,6 +309,8 @@ type Installer interface { var ( // ErrLinked is returned when a linked version cannot be operated on. ErrLinked = errors.New("version is linked") + // ErrNotNeeded is returned when the operation is not needed. + ErrNotNeeded = errors.New("not needed") // ErrNotSupported is returned when the operation is not supported on the platform. ErrNotSupported = errors.New("not supported on this platform") // ErrNotAvailable is returned when the operation is not available at the current version of the platform. @@ -349,8 +325,6 @@ var ( // Process provides an API for interacting with a running Teleport process. type Process interface { - // Name of the process. - Name() string // Reload must reload the Teleport process as gracefully as possible. // If the process is not healthy after reloading, Reload must return an error. // If the process did not require reloading, Reload must return ErrNotNeeded. @@ -500,8 +474,7 @@ func (u *Updater) Install(ctx context.Context, override OverrideConfig) (err err } u.Log.InfoContext(ctx, "Configuration updated.") u.LogConfigWarnings(ctx, cfg.Spec.Path) - u.teleportNotices(ctx) - u.tbotNotices(ctx) + u.notices(ctx) return nil } @@ -550,7 +523,7 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { // it is clear we are not going to recover the package's systemd service if it // was overwritten. if filepath.Clean(cfg.Spec.Path) != filepath.Clean(defaultPathDir) { - if u.TeleportProcess.Name() == teleportServiceName { + if u.TeleportServiceName == serviceName { if !force { u.Log.ErrorContext(ctx, "Default Teleport systemd service would be removed, and --force was not passed.") u.Log.ErrorContext(ctx, "Refusing to remove Teleport from this system.") @@ -581,27 +554,12 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { u.Log.InfoContext(ctx, "Updater-managed installation of Teleport detected.") u.Log.InfoContext(ctx, "Restoring packaged version of Teleport before removing.") - // Restart teleport and tbot. - // If a tbot service is present, and it was created by the updater, - // then we reuse the service for the package to avoid disruption. - - customTbot, err := u.HasCustomTbot(ctx) - if err != nil { - customTbot = true - u.Log.ErrorContext(ctx, "Failed to determine if a custom tbot service is installed.", errorKey, err) - u.Log.ErrorContext(ctx, "Tbot will not be restarted by the updater after the package is restored.") - } - pg := ProcessGroup{u.TeleportProcess} - if !customTbot { - pg = append(pg, u.TbotProcess) - } - revertConfig := func(ctx context.Context) bool { if ok := revert(ctx); !ok { u.Log.ErrorContext(ctx, "Failed to revert Teleport symlinks. Installation likely broken.") return false } - if err := pg.Sync(ctx); err != nil { + if err := u.Process.Sync(ctx); err != nil { u.Log.ErrorContext(ctx, "Failed to revert systemd configuration after failed restart.", errorKey, err) return false } @@ -610,7 +568,7 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { // Sync systemd. - err = pg.Sync(ctx) + err = u.Process.Sync(ctx) if errors.Is(err, context.Canceled) { return trace.Errorf("sync canceled") } @@ -625,19 +583,22 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { return trace.Wrap(err, "failed to validate configuration for system package version of Teleport") } + // Restart Teleport. + u.Log.InfoContext(ctx, "Teleport package successfully restored.") - err = pg.Reload(ctx) + err = u.Process.Reload(ctx) if errors.Is(err, context.Canceled) { return trace.Errorf("reload canceled") } if err != nil && + !errors.Is(err, ErrNotNeeded) && // no output if restart not needed !errors.Is(err, ErrNotSupported) { // already logged above for Sync - // If reloading at the new version fails, revert and reload again. + // If reloading Teleport at the new version fails, revert and reload. u.Log.ErrorContext(ctx, "Reverting symlinks due to failed restart.") if ok := revertConfig(ctx); ok { - if err := pg.Reload(ctx); err != nil { - u.Log.ErrorContext(ctx, "Failed to reload after reverting.", errorKey, err, "service", pg.Name()) + if err := u.Process.Reload(ctx); err != nil && !errors.Is(err, ErrNotNeeded) { + u.Log.ErrorContext(ctx, "Failed to reload Teleport after reverting.", errorKey, err) u.Log.ErrorContext(ctx, "Installation likely broken.") } else { u.Log.WarnContext(ctx, "Teleport updater detected an error with the new installation and successfully reverted it.") @@ -656,16 +617,12 @@ func (u *Updater) Remove(ctx context.Context, force bool) error { func (u *Updater) removeWithoutSystem(ctx context.Context, cfg *UpdateConfig) error { u.Log.InfoContext(ctx, "Updater-managed installation of Teleport detected.") u.Log.InfoContext(ctx, "Attempting to unlink and remove.") - - // Note: tbot.service could be from an unrelated installation, but we should still fail to be safe. - // This would only be an issue for primary installations on PATH, where removal will likely break tbot. - pg := ProcessGroup{u.TeleportProcess, u.TbotProcess} - ok, err := pg.IsActive(ctx) + ok, err := u.Process.IsActive(ctx) if err != nil && !errors.Is(err, ErrNotSupported) { return trace.Wrap(err) } if ok { - return trace.Errorf("refusing to remove active installation, please stop and disable %s first", pg.Name()) + return trace.Errorf("refusing to remove active installation of Teleport, please stop and disable Teleport first") } if err := u.Installer.Unlink(ctx, cfg.Status.Active, cfg.Spec.Path); err != nil { return trace.Wrap(err) @@ -949,10 +906,9 @@ func (u *Updater) Update(ctx context.Context, now bool) error { } else { u.Log.InfoContext(ctx, "Configuration updated.") } - // Show teleportNotices last + // Show notices last if updateErr == nil && now { - u.teleportNotices(ctx) - u.tbotNotices(ctx) + u.notices(ctx) } return trace.NewAggregate(updateErr, writeErr) } @@ -1016,7 +972,7 @@ func (u *Updater) removeRevision(ctx context.Context, cfg *UpdateConfig, rev Rev return trace.Wrap(u.Installer.Remove(ctx, rev)) } -func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision, overwrite, agpl bool) error { +func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision, force, agpl bool) error { baseURL := cfg.Spec.BaseURL if baseURL == "" { if agpl { @@ -1057,31 +1013,11 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // Cleanup logic at the end of this function will ensure that they are removed // eventually. - revert, err := u.Installer.Link(ctx, target, cfg.Spec.Path, overwrite) + revert, err := u.Installer.Link(ctx, target, cfg.Spec.Path, force) if err != nil { return trace.Wrap(err, "failed to link") } - ignoreTbot := false - if customTbot, err := u.HasCustomTbot(ctx); err != nil { - u.Log.ErrorContext(ctx, "Failed to determine if a custom tbot service is installed.", errorKey, err) - u.Log.ErrorContext(ctx, "Tbot will not be managed by the updater.") - ignoreTbot = true - } else if overwrite && customTbot { - u.Log.WarnContext(ctx, "Custom tbot service will be overwritten by the updater.") - ignoreTbot = false - } else if customTbot { - u.Log.WarnContext(ctx, "Unable to manage tbot process due to custom tbot.service file.") - u.Log.WarnContext(ctx, "Pass --overwrite to delete and replace tbot.service with an updater-managed copy.") - u.Log.InfoContext(ctx, "The updater-managed copy may still be modified using systemd drop-in files.") - ignoreTbot = true - } - - pg := ProcessGroup{u.TeleportProcess} - if !ignoreTbot { - pg = append(pg, u.TbotProcess) - } - // If we fail to revert after this point, the next update/enable will // fix the link to restore the active version. @@ -1089,7 +1025,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision if target.Version != "" { cfg.Status.Skip = toPtr(target) } - if overwrite { + if force { u.Log.ErrorContext(ctx, "Unable to revert Teleport symlinks in overwrite mode. Installation likely broken.") return false } @@ -1108,7 +1044,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If re-linking the same version, do not attempt to restart services. if cfg.Status.Active == target { - err := u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, false, !ignoreTbot) + err := u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, false) if errors.Is(err, context.Canceled) { return trace.Errorf("check canceled") } @@ -1129,7 +1065,7 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If a new version was linked, restart services (including on revert). - err = u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, true, !ignoreTbot) + err = u.ReexecSetup(ctx, cfg.Spec.Path, target, cfg.Spec.SELinuxSSH, true) if errors.Is(err, context.Canceled) { return trace.Errorf("check canceled") } @@ -1137,8 +1073,8 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // If reloading Teleport at the new version fails, revert and reload. u.Log.ErrorContext(ctx, "Reverting symlinks due to failed restart.") if ok := revertConfig(ctx); ok { - if err := pg.Reload(ctx); err != nil { - u.Log.ErrorContext(ctx, "Failed to reload services after reverting. Installation likely broken.", errorKey, err) + if err := u.Process.Reload(ctx); err != nil && !errors.Is(err, ErrNotNeeded) { + u.Log.ErrorContext(ctx, "Failed to reload Teleport after reverting. Installation likely broken.", errorKey, err) } else { u.Log.WarnContext(ctx, "Teleport updater detected an error with the new installation and successfully reverted it.") } @@ -1160,20 +1096,14 @@ func (u *Updater) update(ctx context.Context, cfg *UpdateConfig, target Revision // Setup writes updater configuration and verifies the Teleport installation. // If restart is true, Setup also restarts Teleport. // Setup is safe to run concurrently with other Updater commands. -func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installSELinux, restart, tbot bool) error { +func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installSELinux, restart bool) error { + + // Write Teleport systemd service. - // Write teleport systemd service. if err := u.WriteTeleportService(ctx, path, rev); err != nil { return trace.Wrap(err, "failed to write teleport systemd service") } - // Write tbot systemd service. - if tbot { - if err := u.WriteTbotService(ctx, path, rev); err != nil { - return trace.Wrap(err, "failed to write teleport systemd service") - } - } - // Setup teleport-updater configuration and sync systemd. err := u.SetupNamespace(ctx, path, rev, installSELinux) if errors.Is(err, context.Canceled) { @@ -1184,68 +1114,41 @@ func (u *Updater) Setup(ctx context.Context, path string, rev Revision, installS } // Validations - teleportPresent, err := u.validateProcess(ctx, u.TeleportProcess) - if err != nil { - return trace.Wrap(err) - } - tbotPresent, err := u.validateProcess(ctx, u.TbotProcess) - if err != nil { - return trace.Wrap(err) - } - - // Restart Teleport only if necessary. - if !restart { - return nil - } - - if !teleportPresent || !tbotPresent { - u.Log.ErrorContext(ctx, "Missing services will not be restarted.") - } - - var pg ProcessGroup - if teleportPresent { - pg = append(pg, u.TeleportProcess) - } - if tbotPresent && tbot { - pg = append(pg, u.TbotProcess) - } - - err = pg.Reload(ctx) - if errors.Is(err, context.Canceled) { - return trace.Errorf("reload canceled") - } - if err != nil { - return trace.Wrap(err) - } - return nil -} -func (u *Updater) validateProcess(ctx context.Context, p Process) (bool, error) { - present, err := p.IsPresent(ctx) + present, err := u.Process.IsPresent(ctx) if errors.Is(err, context.Canceled) { - return false, trace.Errorf("config check canceled") + return trace.Errorf("config check canceled") } if errors.Is(err, ErrNotSupported) { - u.Log.WarnContext(ctx, "Skipping systemd setup because systemd is not running.", "service", p.Name()) - return false, nil + u.Log.WarnContext(ctx, "Skipping all systemd setup because systemd is not running.") + return nil } if errors.Is(err, ErrNotAvailable) { - u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping service verification.", "service", p.Name()) - return true, nil - } - if err != nil { - return false, trace.Wrap(err, "failed to determine if the new version has an installed systemd service", "service", p.Name()) + u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping SELinux verification.") + } else if err != nil { + return trace.Wrap(err, "failed to determine if new version of Teleport has an installed systemd service") + } else if !present { + return trace.Errorf("cannot find systemd service for new version of Teleport, check SELinux settings") } - if !present { - u.Log.ErrorContext(ctx, "Cannot find systemd service for the new version, check SELinux settings.", "service", p.Name()) - return false, nil + + // Restart Teleport if necessary. + + if restart { + err = u.Process.Reload(ctx) + if errors.Is(err, context.Canceled) { + return trace.Errorf("reload canceled") + } + if err != nil && + !errors.Is(err, ErrNotNeeded) { // skip if not needed + return trace.Wrap(err, "failed to reload Teleport") + } } - return true, nil + return nil } -// teleportNotices displays final notices for teleport after install or update. -func (u *Updater) teleportNotices(ctx context.Context) { - enabled, err := u.TeleportProcess.IsEnabled(ctx) +// notices displays final notices after install or update. +func (u *Updater) notices(ctx context.Context) { + enabled, err := u.Process.IsEnabled(ctx) if errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Teleport is installed, but systemd is not present to start it.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, your system must also be configured to start Teleport.") @@ -1259,7 +1162,7 @@ func (u *Updater) teleportNotices(ctx context.Context) { u.Log.ErrorContext(ctx, "Failed to determine if Teleport is enabled.", errorKey, err) return } - active, err := u.TeleportProcess.IsActive(ctx) + active, err := u.Process.IsActive(ctx) if err != nil { u.Log.ErrorContext(ctx, "Failed to determine if Teleport is active.", errorKey, err) return @@ -1267,52 +1170,17 @@ func (u *Updater) teleportNotices(ctx context.Context) { if !enabled && active { u.Log.WarnContext(ctx, "Teleport is installed and started, but not configured to start on boot.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must enable it.", - "command", "systemctl enable "+u.TeleportProcess.Name()) + "command", "systemctl enable "+u.TeleportServiceName) } if !active && enabled { u.Log.WarnContext(ctx, "Teleport is installed and enabled at boot, but not running.") u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must start it.", - "command", "systemctl start "+u.TeleportProcess.Name()) + "command", "systemctl start "+u.TeleportServiceName) } if !active && !enabled { u.Log.WarnContext(ctx, "Teleport is installed, but not running or enabled at boot.") - u.Log.WarnContext(ctx, "To enable and start Teleport, configure teleport.yaml and run systemctl.", - "command", "systemctl enable --now "+u.TeleportProcess.Name()) - } -} - -// tbotNotices displays final notices for tbot after install or update. -func (u *Updater) tbotNotices(ctx context.Context) { - enabled, err := u.TbotProcess.IsEnabled(ctx) - if errors.Is(err, ErrNotSupported) || - errors.Is(err, ErrNotAvailable) { - // Ignore for tbot, as the corresponding teleport error will cover it. - return - } - if err != nil { - u.Log.ErrorContext(ctx, "Failed to determine if tbot is enabled.", errorKey, err) - return - } - active, err := u.TbotProcess.IsActive(ctx) - if err != nil { - u.Log.ErrorContext(ctx, "Failed to determine if tbot is active.", errorKey, err) - return - } - if !enabled && active { - u.Log.InfoContext(ctx, "The tbot MachineID client installed and started, but not configured to start on boot.") - u.Log.WarnContext(ctx, "After configuring tbot.yaml, you must enable it.", - "command", "systemctl enable "+u.TbotProcess.Name()) - } - if !active && enabled { - u.Log.WarnContext(ctx, "The tbot MachineID client is installed and enabled at boot, but not running.") - u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must start it.", - "command", "systemctl start "+u.TbotProcess.Name()) - } - if !active && !enabled { - // Info-level as many installations will be agent-only. - u.Log.InfoContext(ctx, "The tbot MachineID client installed, but not running or enabled at boot.") - u.Log.InfoContext(ctx, "To enable and start tbot, configure tbot.yaml and run systemctl.", - "command", "systemctl enable --now "+u.TbotProcess.Name()) + u.Log.WarnContext(ctx, "After configuring teleport.yaml, you must enable and start.", + "command", "systemctl enable --now "+u.TeleportServiceName) } } @@ -1380,22 +1248,19 @@ func (u *Updater) LinkPackage(ctx context.Context) error { // If syncing succeeds, ensure the installed systemd service can be found via systemctl. // SELinux contexts can interfere with systemctl's ability to read service files. - err = u.TeleportProcess.Sync(ctx) - if errors.Is(err, ErrNotSupported) { + if err := u.Process.Sync(ctx); errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Systemd is not installed. Skipping sync.") - u.Log.InfoContext(ctx, "Successfully linked system package installation.") - return nil - } - if err != nil { + } else if err != nil { return trace.Wrap(err, "failed to sync systemd configuration") - } - - present, err := u.validateProcess(ctx, u.TeleportProcess) - if err != nil { - return trace.Wrap(err) - } - if !present { - return trace.Errorf("missing Teleport service") + } else { + present, err := u.Process.IsPresent(ctx) + if errors.Is(err, ErrNotAvailable) { + u.Log.DebugContext(ctx, "Systemd version is outdated. Skipping SELinux verification.") + } else if err != nil { + return trace.Wrap(err, "failed to determine if Teleport has an installed systemd service") + } else if !present { + return trace.Errorf("cannot find systemd service for Teleport, check SELinux settings") + } } u.Log.InfoContext(ctx, "Successfully linked system package installation.") return nil @@ -1407,7 +1272,7 @@ func (u *Updater) UnlinkPackage(ctx context.Context) error { if err := u.Installer.UnlinkSystem(ctx); err != nil { return trace.Wrap(err, "failed to unlink system package installation") } - if err := u.TeleportProcess.Sync(ctx); errors.Is(err, ErrNotSupported) { + if err := u.Process.Sync(ctx); errors.Is(err, ErrNotSupported) { u.Log.WarnContext(ctx, "Systemd is not installed. Skipping sync.") } else if err != nil { return trace.Wrap(err, "failed to sync systemd configuration") diff --git a/lib/autoupdate/agent/updater_test.go b/lib/autoupdate/agent/updater_test.go index 20fff13c97717..bda5ea6c4b0d3 100644 --- a/lib/autoupdate/agent/updater_test.go +++ b/lib/autoupdate/agent/updater_test.go @@ -730,189 +730,157 @@ func TestUpdater_Update(t *testing.T) { } for _, tt := range tests { - for _, tbot := range []bool{false, true} { - t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { - var requestedGroup string - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestedGroup = r.URL.Query().Get("group") - config := webclient.PingResponse{ - AutoUpdate: webclient.AutoUpdateSettings{ - AgentVersion: "16.3.0", - AgentAutoUpdate: tt.inWindow, - }, - } - config.Edition = "community" - if tt.flags&autoupdate.FlagEnterprise != 0 { - config.Edition = "ent" - } - if tt.agpl { - config.Edition = "oss" - } - config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 - err := json.NewEncoder(w).Encode(config) - require.NoError(t, err) - })) - t.Cleanup(server.Close) - - dir := t.TempDir() - ns := &Namespace{ - installDir: dir, - defaultPathDir: "ignored", - updaterIDFile: "updater-id-file", + t.Run(tt.name, func(t *testing.T) { + var requestedGroup string + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestedGroup = r.URL.Query().Get("group") + config := webclient.PingResponse{ + AutoUpdate: webclient.AutoUpdateSettings{ + AgentVersion: "16.3.0", + AgentAutoUpdate: tt.inWindow, + }, } - _, err := ns.Init() + config.Edition = "community" + if tt.flags&autoupdate.FlagEnterprise != 0 { + config.Edition = "ent" + } + if tt.agpl { + config.Edition = "oss" + } + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 + err := json.NewEncoder(w).Encode(config) require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) + })) + t.Cleanup(server.Close) - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) + dir := t.TempDir() + ns := &Namespace{ + installDir: dir, + defaultPathDir: "ignored", + updaterIDFile: "updater-id-file", + } + _, err := ns.Init() + require.NoError(t, err) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) - // Create config file only if provided in test case - if tt.cfg != nil { - tt.cfg.Spec.Proxy = strings.TrimPrefix(server.URL, "https://") - b, err := yaml.Marshal(tt.cfg) - require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) - require.NoError(t, err) - } + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) + require.NoError(t, err) + + // Create config file only if provided in test case + if tt.cfg != nil { + tt.cfg.Spec.Proxy = strings.TrimPrefix(server.URL, "https://") + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } - var ( - installedRevision Revision - installedBaseURL string - linkedRevision Revision - removedRevisions []Revision - revertFuncCalls int - setupCalls int - revertSetupCalls int - teleportReloadCalls int - tbotReloadCalls int - ) - updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { - for _, r := range tt.linkedRevisions { - if r == rev { - require.False(t, force) - } + var ( + installedRevision Revision + installedBaseURL string + linkedRevision Revision + removedRevisions []Revision + revertFuncCalls int + setupCalls int + revertSetupCalls int + reloadCalls int + ) + updater.Installer = &testInstaller{ + FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { + for _, r := range tt.linkedRevisions { + if r == rev { + require.False(t, force) } - installedRevision = rev - installedBaseURL = baseURL - return tt.installErr - }, - FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { - linkedRevision = rev - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, nil - }, - FuncList: func(_ context.Context) (revs []Revision, err error) { - return slices.Compact([]Revision{ - installedRevision, - tt.cfg.Status.Active, - NewRevision("unknown-version", 0), - }), nil - }, - FuncRemove: func(_ context.Context, rev Revision) error { - removedRevisions = append(removedRevisions, rev) - return nil - }, - FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { - return slices.Contains(tt.linkedRevisions, rev), nil - }, - } - updater.TeleportProcess = &testProcess{ - FuncName: func() string { - return "teleport" - }, - FuncReload: func(_ context.Context) error { - teleportReloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return true, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil - }, - } - updater.TbotProcess = &testProcess{ - FuncName: func() string { - return "tbot" - }, - FuncReload: func(_ context.Context) error { - tbotReloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return true, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil - }, - } - var restarted bool - updater.ReexecSetup = func(_ context.Context, path string, rev Revision, _, reload, _ bool) error { - restarted = reload - setupCalls++ - return tt.setupErr - } - updater.SetupNamespace = func(_ context.Context, path string, rev Revision, _ bool) error { - revertSetupCalls++ + } + installedRevision = rev + installedBaseURL = baseURL + return tt.installErr + }, + FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { + linkedRevision = rev + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, nil + }, + FuncList: func(_ context.Context) (revs []Revision, err error) { + return slices.Compact([]Revision{ + installedRevision, + tt.cfg.Status.Active, + NewRevision("unknown-version", 0), + }), nil + }, + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevisions = append(removedRevisions, rev) return nil - } - updater.HasCustomTbot = func(ctx context.Context) (bool, error) { - return !tbot, nil - } - - ctx := context.Background() - err = updater.Update(ctx, tt.now) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedBaseURL, installedBaseURL) - require.Equal(t, tt.linkedRevision, linkedRevision) - require.Equal(t, tt.removedRevisions, removedRevisions) - require.Equal(t, tt.flags, installedRevision.Flags) - require.Equal(t, tt.requestGroup, requestedGroup) - require.Equal(t, tt.reloadCalls, teleportReloadCalls) - if tbot && tt.reloadErr == nil { - require.Equal(t, tt.reloadCalls, tbotReloadCalls) - } else { - require.Equal(t, 0, tbotReloadCalls) - } - require.Equal(t, tt.revertCalls, revertSetupCalls) - require.Equal(t, tt.revertCalls, revertFuncCalls) - require.Equal(t, tt.setupCalls, setupCalls) - require.Equal(t, tt.restarted, restarted) - - if tt.cfg == nil { - _, err := os.Stat(cfgPath) - require.Error(t, err) - return - } + }, + FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { + return slices.Contains(tt.linkedRevisions, rev), nil + }, + } + updater.Process = &testProcess{ + FuncReload: func(_ context.Context) error { + reloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return true, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil + }, + } + var restarted bool + updater.ReexecSetup = func(_ context.Context, path string, rev Revision, _ bool, reload bool) error { + restarted = reload + setupCalls++ + return tt.setupErr + } + updater.SetupNamespace = func(_ context.Context, path string, rev Revision, _ bool) error { + revertSetupCalls++ + return nil + } - data, err := os.ReadFile(cfgPath) + ctx := context.Background() + err = updater.Update(ctx, tt.now) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { require.NoError(t, err) - data = blankTestAddr(data) + } + require.Equal(t, tt.installedRevision, installedRevision) + require.Equal(t, tt.installedBaseURL, installedBaseURL) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevisions, removedRevisions) + require.Equal(t, tt.flags, installedRevision.Flags) + require.Equal(t, tt.requestGroup, requestedGroup) + require.Equal(t, tt.reloadCalls, reloadCalls) + require.Equal(t, tt.revertCalls, revertSetupCalls) + require.Equal(t, tt.revertCalls, revertFuncCalls) + require.Equal(t, tt.setupCalls, setupCalls) + require.Equal(t, tt.restarted, restarted) - if golden.ShouldSet() { - golden.Set(t, data) - } - require.Equal(t, string(golden.Get(t)), string(data)) - }) - } + if tt.cfg == nil { + _, err := os.Stat(cfgPath) + require.Error(t, err) + return + } + + data, err := os.ReadFile(cfgPath) + require.NoError(t, err) + data = blankTestAddr(data) + + if golden.ShouldSet() { + golden.Set(t, data) + } + require.Equal(t, string(golden.Get(t)), string(data)) + }) } } @@ -1034,7 +1002,7 @@ func TestUpdater_LinkPackage(t *testing.T) { tryLinkSystemCalls: 1, syncCalls: 1, notPresent: true, - errMatch: "missing Teleport service", + errMatch: "cannot find systemd service", }, } @@ -1067,10 +1035,7 @@ func TestUpdater_LinkPackage(t *testing.T) { }, } var syncCalls int - updater.TeleportProcess = &testProcess{ - FuncName: func() string { - return "test" - }, + updater.Process = &testProcess{ FuncSync: func(_ context.Context) error { syncCalls++ return tt.syncErr @@ -1278,23 +1243,6 @@ func TestUpdater_Remove(t *testing.T) { reloadCalls: 1, teardownCalls: 1, }, - { - name: "active version with tbot", - cfg: &UpdateConfig{ - Version: updateConfigVersion, - Kind: updateConfigKind, - Spec: UpdateSpec{ - Path: defaultPathDir, - }, - Status: UpdateStatus{ - Active: NewRevision(version, 0), - }, - }, - linkSystemCalls: 1, - syncCalls: 1, - reloadCalls: 1, - teardownCalls: 1, - }, { name: "active version, no systemd", cfg: &UpdateConfig{ @@ -1330,6 +1278,7 @@ func TestUpdater_Remove(t *testing.T) { syncCalls: 1, reloadCalls: 1, teardownCalls: 1, + reloadErr: ErrNotNeeded, }, { name: "active version, sync error", @@ -1371,114 +1320,84 @@ func TestUpdater_Remove(t *testing.T) { } for _, tt := range tests { - for _, tbot := range []bool{false, true} { - t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { - dir := t.TempDir() - ns := &Namespace{installDir: dir} - _, err := ns.Init() - require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) + t.Run(tt.name, func(t *testing.T) { + dir := t.TempDir() + ns := &Namespace{installDir: dir} + _, err := ns.Init() + require.NoError(t, err) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) + require.NoError(t, err) + updater.TeleportServiceName = serviceName + if tt.serviceName != "" { + updater.TeleportServiceName = tt.serviceName + } - // Create config file only if provided in test case - if tt.cfg != nil { - b, err := yaml.Marshal(tt.cfg) - require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) - require.NoError(t, err) - } + // Create config file only if provided in test case + if tt.cfg != nil { + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } - var ( - linkSystemCalls int - revertFuncCalls int - teleportSyncCalls int - teleportReloadCalls int - tbotSyncCalls int - tbotReloadCalls int - teardownCalls int - unlinkedVersion string - ) - updater.Installer = &testInstaller{ - FuncLinkSystem: func(_ context.Context) (revert func(context.Context) bool, err error) { - linkSystemCalls++ - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, tt.linkSystemErr - }, - FuncUnlink: func(_ context.Context, rev Revision, path string) error { - unlinkedVersion = rev.Version - return nil - }, - } - updater.TeleportProcess = &testProcess{ - FuncName: func() string { - if tt.serviceName != "" { - return tt.serviceName - } - return teleportServiceName - }, - FuncSync: func(_ context.Context) error { - teleportSyncCalls++ - return tt.syncErr - }, - FuncReload: func(_ context.Context) error { - teleportReloadCalls++ - return tt.reloadErr - }, - FuncIsActive: func(_ context.Context) (bool, error) { - return tt.processActive, tt.isActiveErr - }, - } - updater.TbotProcess = &testProcess{ - FuncSync: func(_ context.Context) error { - tbotSyncCalls++ - return tt.syncErr - }, - FuncReload: func(_ context.Context) error { - tbotReloadCalls++ - return tt.reloadErr - }, - FuncIsActive: func(_ context.Context) (bool, error) { - return tt.processActive, tt.isActiveErr - }, - } - updater.TeardownNamespace = func(_ context.Context) error { - teardownCalls++ + var ( + linkSystemCalls int + revertFuncCalls int + syncCalls int + reloadCalls int + teardownCalls int + unlinkedVersion string + ) + updater.Installer = &testInstaller{ + FuncLinkSystem: func(_ context.Context) (revert func(context.Context) bool, err error) { + linkSystemCalls++ + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, tt.linkSystemErr + }, + FuncUnlink: func(_ context.Context, rev Revision, path string) error { + unlinkedVersion = rev.Version return nil - } - updater.HasCustomTbot = func(ctx context.Context) (bool, error) { - return !tbot, nil - } - - ctx := context.Background() - err = updater.Remove(ctx, tt.force) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { - require.NoError(t, err) - } - require.Equal(t, tt.syncCalls, teleportSyncCalls) - require.Equal(t, tt.reloadCalls, teleportReloadCalls) - require.Equal(t, 0, tbotSyncCalls) - if tbot && tt.reloadErr == nil { - require.Equal(t, tt.reloadCalls, tbotReloadCalls) - } else { - require.Equal(t, 0, tbotReloadCalls) - } - - require.Equal(t, tt.linkSystemCalls, linkSystemCalls) - require.Equal(t, tt.revertFuncCalls, revertFuncCalls) - require.Equal(t, tt.unlinkedVersion, unlinkedVersion) - require.Equal(t, tt.teardownCalls, teardownCalls) - }) - } + }, + } + updater.Process = &testProcess{ + FuncSync: func(_ context.Context) error { + syncCalls++ + return tt.syncErr + }, + FuncReload: func(_ context.Context) error { + reloadCalls++ + return tt.reloadErr + }, + FuncIsActive: func(_ context.Context) (bool, error) { + return tt.processActive, tt.isActiveErr + }, + } + updater.TeardownNamespace = func(_ context.Context) error { + teardownCalls++ + return nil + } + ctx := context.Background() + err = updater.Remove(ctx, tt.force) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.syncCalls, syncCalls) + require.Equal(t, tt.reloadCalls, reloadCalls) + require.Equal(t, tt.linkSystemCalls, linkSystemCalls) + require.Equal(t, tt.revertFuncCalls, revertFuncCalls) + require.Equal(t, tt.unlinkedVersion, unlinkedVersion) + require.Equal(t, tt.teardownCalls, teardownCalls) + }) } } @@ -1749,7 +1668,9 @@ func TestUpdater_Install(t *testing.T) { errMatch: "setup error", }, { - name: "no need to reload", + name: "no need to reload", + reloadErr: ErrNotNeeded, + installedRevision: NewRevision("16.3.0", 0), installedBaseURL: autoupdate.DefaultBaseURL, linkedRevision: NewRevision("16.3.0", 0), @@ -1822,194 +1743,161 @@ func TestUpdater_Install(t *testing.T) { } for _, tt := range tests { - for _, tbot := range []bool{false, true} { - t.Run(fmt.Sprintf("%s tbot=%t", tt.name, tbot), func(t *testing.T) { - if len(tt.restrictOS) > 0 && !slices.Contains(tt.restrictOS, runtime.GOOS) { - t.Skip("skipping test because OS is not supported") - } - dir := t.TempDir() - ns := &Namespace{ - installDir: dir, - defaultPathDir: defaultPathDir, - defaultProxyAddr: "default-proxy", - updaterIDFile: "updater-id-file", - } - _, err := ns.Init() - require.NoError(t, err) - cfgPath := filepath.Join(ns.Dir(), updateConfigName) + t.Run(tt.name, func(t *testing.T) { + if len(tt.restrictOS) > 0 && !slices.Contains(tt.restrictOS, runtime.GOOS) { + t.Skip("skipping test because OS is not supported") + } + dir := t.TempDir() + ns := &Namespace{ + installDir: dir, + defaultPathDir: defaultPathDir, + defaultProxyAddr: "default-proxy", + updaterIDFile: "updater-id-file", + } + _, err := ns.Init() + require.NoError(t, err) + cfgPath := filepath.Join(ns.Dir(), updateConfigName) - updater, err := NewLocalUpdater(LocalUpdaterConfig{ - InsecureSkipVerify: true, - }, ns) - require.NoError(t, err) + updater, err := NewLocalUpdater(LocalUpdaterConfig{ + InsecureSkipVerify: true, + }, ns) + require.NoError(t, err) - // Create config file only if provided in test case - if tt.cfg != nil { - b, err := yaml.Marshal(tt.cfg) - require.NoError(t, err) - err = os.WriteFile(cfgPath, b, 0600) - require.NoError(t, err) - } + // Create config file only if provided in test case + if tt.cfg != nil { + b, err := yaml.Marshal(tt.cfg) + require.NoError(t, err) + err = os.WriteFile(cfgPath, b, 0600) + require.NoError(t, err) + } - var requestedGroup string - server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - requestedGroup = r.URL.Query().Get("group") - config := webclient.PingResponse{ - AutoUpdate: webclient.AutoUpdateSettings{ - AgentVersion: "16.3.0", - }, - } - config.Edition = "community" - if tt.flags&autoupdate.FlagEnterprise != 0 { - config.Edition = "ent" - } - if tt.agpl { - config.Edition = "oss" - } - config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 - err := json.NewEncoder(w).Encode(config) - require.NoError(t, err) - })) - t.Cleanup(server.Close) - - userCfg := tt.userCfg - if userCfg.Proxy == "" { - userCfg.Proxy = strings.TrimPrefix(server.URL, "https://") - } - updater.DefaultProxyAddr = userCfg.Proxy - - var ( - installedRevision Revision - installedBaseURL string - linkedRevision Revision - removedRevision Revision - revertFuncCalls int - teleportReloadCalls int - tbotReloadCalls int - setupCalls int - revertSetupCalls int - selinuxInstalls int - selinuxRemovals int - ) - updater.Installer = &testInstaller{ - FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { - installedRevision = rev - installedBaseURL = baseURL - return tt.installErr - }, - FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { - linkedRevision = rev - return func(_ context.Context) bool { - revertFuncCalls++ - return true - }, nil - }, - FuncList: func(_ context.Context) (revs []Revision, err error) { - return []Revision{}, nil - }, - FuncRemove: func(_ context.Context, rev Revision) error { - removedRevision = rev - return nil - }, - FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { - return false, nil - }, - } - updater.TeleportProcess = &testProcess{ - FuncName: func() string { - return "teleport" - }, - FuncReload: func(_ context.Context) error { - teleportReloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return !tt.notPresent, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil - }, - } - updater.TbotProcess = &testProcess{ - FuncName: func() string { - return "tbot" - }, - FuncReload: func(_ context.Context) error { - tbotReloadCalls++ - return tt.reloadErr - }, - FuncIsPresent: func(ctx context.Context) (bool, error) { - return !tt.notPresent, nil - }, - FuncIsEnabled: func(ctx context.Context) (bool, error) { - return !tt.notEnabled, nil - }, - FuncIsActive: func(ctx context.Context) (bool, error) { - return !tt.notActive, nil + var requestedGroup string + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestedGroup = r.URL.Query().Get("group") + config := webclient.PingResponse{ + AutoUpdate: webclient.AutoUpdateSettings{ + AgentVersion: "16.3.0", }, } - var restarted bool - updater.ReexecSetup = func(_ context.Context, path string, rev Revision, installSELinux, reload, tbot bool) error { - setupCalls++ - if installSELinux { - selinuxInstalls++ - } - restarted = reload - return tt.setupErr - } - updater.SetupNamespace = func(_ context.Context, path string, rev Revision, installSELinux bool) error { - revertSetupCalls++ - if installSELinux { - selinuxInstalls++ - } else { - selinuxRemovals++ - } - return nil + config.Edition = "community" + if tt.flags&autoupdate.FlagEnterprise != 0 { + config.Edition = "ent" } - updater.HasCustomTbot = func(ctx context.Context) (bool, error) { - return !tbot, nil + if tt.agpl { + config.Edition = "oss" } + config.FIPS = tt.flags&autoupdate.FlagFIPS != 0 + err := json.NewEncoder(w).Encode(config) + require.NoError(t, err) + })) + t.Cleanup(server.Close) - ctx := context.Background() - err = updater.Install(ctx, userCfg) - if tt.errMatch != "" { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.errMatch) - } else { - require.NoError(t, err) + if tt.userCfg.Proxy == "" { + tt.userCfg.Proxy = strings.TrimPrefix(server.URL, "https://") + } + updater.DefaultProxyAddr = tt.userCfg.Proxy + + var ( + installedRevision Revision + installedBaseURL string + linkedRevision Revision + removedRevision Revision + revertFuncCalls int + reloadCalls int + setupCalls int + revertSetupCalls int + selinuxInstalls int + selinuxRemovals int + ) + updater.Installer = &testInstaller{ + FuncInstall: func(_ context.Context, rev Revision, baseURL string, force bool) error { + installedRevision = rev + installedBaseURL = baseURL + return tt.installErr + }, + FuncLink: func(_ context.Context, rev Revision, path string, force bool) (revert func(context.Context) bool, err error) { + linkedRevision = rev + return func(_ context.Context) bool { + revertFuncCalls++ + return true + }, nil + }, + FuncList: func(_ context.Context) (revs []Revision, err error) { + return []Revision{}, nil + }, + FuncRemove: func(_ context.Context, rev Revision) error { + removedRevision = rev + return nil + }, + FuncIsLinked: func(ctx context.Context, rev Revision, path string) (bool, error) { + return false, nil + }, + } + updater.Process = &testProcess{ + FuncReload: func(_ context.Context) error { + reloadCalls++ + return tt.reloadErr + }, + FuncIsPresent: func(ctx context.Context) (bool, error) { + return !tt.notPresent, nil + }, + FuncIsEnabled: func(ctx context.Context) (bool, error) { + return !tt.notEnabled, nil + }, + FuncIsActive: func(ctx context.Context) (bool, error) { + return !tt.notActive, nil + }, + } + var restarted bool + updater.ReexecSetup = func(_ context.Context, path string, rev Revision, installSELinux bool, reload bool) error { + setupCalls++ + if installSELinux { + selinuxInstalls++ } - require.Equal(t, tt.installedRevision, installedRevision) - require.Equal(t, tt.installedBaseURL, installedBaseURL) - require.Equal(t, tt.linkedRevision, linkedRevision) - require.Equal(t, tt.removedRevision, removedRevision) - require.Equal(t, tt.flags, installedRevision.Flags) - require.Equal(t, tt.requestGroup, requestedGroup) - require.Equal(t, tt.reloadCalls, teleportReloadCalls) - if tbot && tt.reloadErr == nil { - require.Equal(t, tt.reloadCalls, tbotReloadCalls) + restarted = reload + return tt.setupErr + } + updater.SetupNamespace = func(_ context.Context, path string, rev Revision, installSELinux bool) error { + revertSetupCalls++ + if installSELinux { + selinuxInstalls++ } else { - require.Equal(t, 0, tbotReloadCalls) + selinuxRemovals++ } - require.Equal(t, tt.revertCalls, revertSetupCalls) - require.Equal(t, tt.revertCalls, revertFuncCalls) - require.Equal(t, tt.setupCalls, setupCalls) - require.Equal(t, tt.selinuxInstalls, selinuxInstalls) - require.Equal(t, tt.selinuxRemovals, selinuxRemovals) - require.Equal(t, tt.restarted, restarted) - - data, err := os.ReadFile(cfgPath) + return nil + } + + ctx := context.Background() + err = updater.Install(ctx, tt.userCfg) + if tt.errMatch != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.errMatch) + } else { require.NoError(t, err) - data = blankTestAddr(data) + } + require.Equal(t, tt.installedRevision, installedRevision) + require.Equal(t, tt.installedBaseURL, installedBaseURL) + require.Equal(t, tt.linkedRevision, linkedRevision) + require.Equal(t, tt.removedRevision, removedRevision) + require.Equal(t, tt.flags, installedRevision.Flags) + require.Equal(t, tt.requestGroup, requestedGroup) + require.Equal(t, tt.reloadCalls, reloadCalls) + require.Equal(t, tt.revertCalls, revertSetupCalls) + require.Equal(t, tt.revertCalls, revertFuncCalls) + require.Equal(t, tt.setupCalls, setupCalls) + require.Equal(t, tt.selinuxInstalls, selinuxInstalls) + require.Equal(t, tt.selinuxRemovals, selinuxRemovals) + require.Equal(t, tt.restarted, restarted) - if golden.ShouldSet() { - golden.Set(t, data) - } - require.Equal(t, string(golden.Get(t)), string(data)) - }) - } + data, err := os.ReadFile(cfgPath) + require.NoError(t, err) + data = blankTestAddr(data) + + if golden.ShouldSet() { + golden.Set(t, data) + } + require.Equal(t, string(golden.Get(t)), string(data)) + }) } } @@ -2093,9 +1981,16 @@ func TestUpdater_Setup(t *testing.T) { present: true, }, { - name: "not present", - restart: true, - present: false, + name: "reload not needed", + restart: true, + present: true, + reloadErr: ErrNotNeeded, + }, + { + name: "not present", + restart: true, + present: false, + errMatch: "cannot find systemd", }, { name: "setup error", @@ -2202,22 +2097,7 @@ func TestUpdater_Setup(t *testing.T) { updater, err := NewLocalUpdater(LocalUpdaterConfig{}, ns) require.NoError(t, err) - updater.TeleportProcess = &testProcess{ - FuncName: func() string { - return "teleport" - }, - FuncReload: func(_ context.Context) error { - return tt.reloadErr - }, - FuncIsPresent: func(_ context.Context) (bool, error) { - return tt.present, tt.presentErr - }, - } - - updater.TbotProcess = &testProcess{ - FuncName: func() string { - return "tbot" - }, + updater.Process = &testProcess{ FuncReload: func(_ context.Context) error { return tt.reloadErr }, @@ -2233,15 +2113,7 @@ func TestUpdater_Setup(t *testing.T) { updater.WriteTeleportService = func(_ context.Context, path string, rev Revision) error { require.Equal(t, "test", path) require.Equal(t, "version", rev.Version) - return nil - } - updater.WriteTbotService = func(_ context.Context, path string, rev Revision) error { - require.Equal(t, "test", path) - require.Equal(t, "version", rev.Version) - return nil - } - updater.HasCustomTbot = func(ctx context.Context) (bool, error) { - return false, nil + return tt.setupErr } // Create config file only if provided in test case @@ -2253,7 +2125,7 @@ func TestUpdater_Setup(t *testing.T) { } ctx := context.Background() - err = updater.Setup(ctx, "test", Revision{Version: "version"}, tt.installSELinux, tt.restart, true) + err = updater.Setup(ctx, "test", Revision{Version: "version"}, tt.installSELinux, tt.restart) if tt.errMatch != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.errMatch) @@ -2421,7 +2293,6 @@ func (ti *testInstaller) IsLinked(ctx context.Context, rev Revision, path string } type testProcess struct { - FuncName func() string FuncReload func(ctx context.Context) error FuncSync func(ctx context.Context) error FuncIsEnabled func(ctx context.Context) (bool, error) @@ -2429,10 +2300,6 @@ type testProcess struct { FuncIsPresent func(ctx context.Context) (bool, error) } -func (tp *testProcess) Name() string { - return tp.FuncName() -} - func (tp *testProcess) Reload(ctx context.Context) error { return tp.FuncReload(ctx) } diff --git a/tool/teleport-update/main.go b/tool/teleport-update/main.go index 0afcc8cb84c5e..f974b675c3675 100644 --- a/tool/teleport-update/main.go +++ b/tool/teleport-update/main.go @@ -98,8 +98,6 @@ type cliConfig struct { Insecure bool // StatusWithExitCode makes the status command return different exit codes depending on the update status. StatusWithExitCode bool - // SetupTbot specifies whether tbot should be managed. - SetupTbot bool } func Run(args []string) int { @@ -131,7 +129,7 @@ func Run(args []string) int { Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) enableCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) - enableCmd.Flag("overwrite", "Allow existing installed binaries and services to be overwritten."). + enableCmd.Flag("overwrite", "Allow existing installed Teleport binaries to be overwritten."). Short('o').BoolVar(&ccfg.AllowOverwrite) enableCmd.Flag("allow-proxy-conflict", "Allow proxy addresses in teleport.yaml and update.yaml to conflict."). BoolVar(&ccfg.AllowProxyConflict) @@ -155,7 +153,7 @@ func Run(args []string) int { Short('g').Envar(updateGroupEnvVar).StringVar(&ccfg.Group) pinCmd.Flag("base-url", "Base URL used to override the Teleport download URL."). Short('b').Envar(common.BaseURLEnvVar).StringVar(&ccfg.BaseURL) - pinCmd.Flag("overwrite", "Allow existing installed binaries and services to be overwritten."). + pinCmd.Flag("overwrite", "Allow existing installed Teleport binaries to be overwritten."). Short('o').BoolVar(&ccfg.AllowOverwrite) pinCmd.Flag("allow-proxy-conflict", "Allow proxy addresses in teleport.yaml and update.yaml to conflict."). BoolVar(&ccfg.AllowProxyConflict) @@ -195,8 +193,6 @@ func Run(args []string) int { Envar(autoupdate.SetupFlagsEnvVar).StringsVar(&ccfg.ForceFlags) setupCmd.Flag("selinux-ssh", "Install the SELinux module for Teleport SSH."). Hidden().Envar(autoupdate.SetupSELinuxSSHEnvVar).BoolVar(&ccfg.SELinuxSSH) - setupCmd.Flag("tbot", "Setup a systemd service for tbot."). - Envar(autoupdate.SetupTbotEnvVar).BoolVar(&ccfg.SetupTbot) statusCmd := app.Command("status", "Show Teleport agent auto-update status.") statusCmd.Flag("err-if-should-update-now", @@ -492,7 +488,7 @@ func cmdSetup(ctx context.Context, ccfg *cliConfig) error { } flags := common.NewInstallFlagsFromStrings(ccfg.ForceFlags) rev := autoupdate.NewRevision(ccfg.ForceVersion, flags) - err = updater.Setup(ctx, ccfg.Path, rev, ccfg.SELinuxSSH, ccfg.Reload, ccfg.SetupTbot) + err = updater.Setup(ctx, ccfg.Path, rev, ccfg.SELinuxSSH, ccfg.Reload) if err != nil { return trace.Wrap(err) } From 1511655bff7f7f313734a707231d663ee84f530e Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Wed, 22 Oct 2025 14:05:33 -0400 Subject: [PATCH 03/16] tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9b36bfa77fcc1..59419758e60db 100644 --- a/go.mod +++ b/go.mod @@ -215,6 +215,7 @@ require ( github.com/sigstore/sigstore v1.9.6-0.20250729224751-181c5d3339b3 github.com/sigstore/sigstore-go v1.1.2 github.com/sijms/go-ora/v2 v2.9.0 + github.com/sirupsen/logrus v1.9.3 github.com/snowflakedb/gosnowflake v1.17.0 github.com/spf13/cobra v1.10.1 github.com/spiffe/go-spiffe/v2 v2.6.0 @@ -564,7 +565,6 @@ require ( github.com/sigstore/rekor v1.4.2 // indirect github.com/sigstore/rekor-tiles v0.1.11 // indirect github.com/sigstore/timestamp-authority v1.2.9 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect From f676054206bcd92bfca1151ad6fa0b2ed3c91635 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Wed, 22 Oct 2025 16:01:04 -0400 Subject: [PATCH 04/16] tidy --- integrations/terraform-mwi/go.sum | 14 -------------- integrations/terraform/go.sum | 14 -------------- 2 files changed, 28 deletions(-) diff --git a/integrations/terraform-mwi/go.sum b/integrations/terraform-mwi/go.sum index 4bd2ad5cb91b9..ebf46b62f63ad 100644 --- a/integrations/terraform-mwi/go.sum +++ b/integrations/terraform-mwi/go.sum @@ -637,8 +637,6 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI= -code.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8= connectrpc.com/connect v1.19.0 h1:LuqUbq01PqbtL0o7vn0WMRXzR2nNsiINe5zfcJ24pJM= connectrpc.com/connect v1.19.0/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= @@ -912,8 +910,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47 github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0 h1:EJsHUYgFBV7/N1YtL73lsfZODAOU+CnNSZfEAlqqQaA= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0/go.mod h1:AxKuXHc0zv2yYaeueUG7R3ONbcnQIuDj0bkdFmPVRzU= -github.com/aws/rolesanywhere-credential-helper v1.2.0 h1:eLqJvSznH8nJk48dwFc0raWOpbTGgBeNYH3Q8UQFVx4= -github.com/aws/rolesanywhere-credential-helper v1.2.0/go.mod h1:YRxmRrAaqbVVXPNH1gHT76nWaMGvpAziHAHw8UwKrpU= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= @@ -1007,8 +1003,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4 h1:NZwojZ3+Cb8qxEkNdtmIrjwergNXvkTjvnM+yecyQoQ= -github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= github.com/coreos/go-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= @@ -1058,10 +1052,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dmarkham/enumer v1.6.1 h1:aSc9awYtZL07TUueWs40QcHtxTvHTAwG0EqrNsK45w4= github.com/dmarkham/enumer v1.6.1/go.mod h1:yixql+kDDQRYqcuBM2n9Vlt7NoT9ixgXhaXry8vmRg8= -github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= -github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= @@ -2070,8 +2060,6 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= -github.com/spiffe/aws-spiffe-workload-helper v0.0.4 h1:P39B0/nXbSHm2WIBkHgKYclGzu2AZVjTA5keEa6CnOk= -github.com/spiffe/aws-spiffe-workload-helper v0.0.4/go.mod h1:off3o6L61qUpoyoHIBSC7W75EVzAwMIRr2Va6Oy9988= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -2140,8 +2128,6 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/transparency-dev/tessera v1.0.0-rc3 h1:v385KqMekDUKI3ZVJHCHE5MAz8LBrWsEKa6OzYLrz0k= github.com/transparency-dev/tessera v1.0.0-rc3/go.mod h1:aaLlvG/sEPMzT96iIF4hua6Z9pLzkfDtkbaUAR4IL8I= -github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= -github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/integrations/terraform/go.sum b/integrations/terraform/go.sum index 2d63ee25e09a6..bcb96a57d509c 100644 --- a/integrations/terraform/go.sum +++ b/integrations/terraform/go.sum @@ -638,8 +638,6 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -code.dny.dev/ssrf v0.2.0 h1:wCBP990rQQ1CYfRpW+YK1+8xhwUjv189AQ3WMo1jQaI= -code.dny.dev/ssrf v0.2.0/go.mod h1:B+91l25OnyaLIeCx0WRJN5qfJ/4/ZTZxRXgm0lj/2w8= connectrpc.com/connect v1.19.0 h1:LuqUbq01PqbtL0o7vn0WMRXzR2nNsiINe5zfcJ24pJM= connectrpc.com/connect v1.19.0/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= @@ -932,8 +930,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47 github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0 h1:EJsHUYgFBV7/N1YtL73lsfZODAOU+CnNSZfEAlqqQaA= github.com/aws/aws-sigv4-auth-cassandra-gocql-driver-plugin v1.1.0/go.mod h1:AxKuXHc0zv2yYaeueUG7R3ONbcnQIuDj0bkdFmPVRzU= -github.com/aws/rolesanywhere-credential-helper v1.2.0 h1:eLqJvSznH8nJk48dwFc0raWOpbTGgBeNYH3Q8UQFVx4= -github.com/aws/rolesanywhere-credential-helper v1.2.0/go.mod h1:YRxmRrAaqbVVXPNH1gHT76nWaMGvpAziHAHw8UwKrpU= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= @@ -1030,8 +1026,6 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4 h1:NZwojZ3+Cb8qxEkNdtmIrjwergNXvkTjvnM+yecyQoQ= -github.com/containerd/stargz-snapshotter/estargz v0.17.1-0.20250814072747-99bfda8ce9c4/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= github.com/coreos/go-oidc v2.3.0+incompatible h1:+5vEsrgprdLjjQ9FzIKAzQz1wwPD+83hQRfUIPh7rO0= github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= @@ -1081,10 +1075,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dmarkham/enumer v1.6.1 h1:aSc9awYtZL07TUueWs40QcHtxTvHTAwG0EqrNsK45w4= github.com/dmarkham/enumer v1.6.1/go.mod h1:yixql+kDDQRYqcuBM2n9Vlt7NoT9ixgXhaXry8vmRg8= -github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= -github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= -github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk= github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= @@ -2152,8 +2142,6 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= -github.com/spiffe/aws-spiffe-workload-helper v0.0.4 h1:P39B0/nXbSHm2WIBkHgKYclGzu2AZVjTA5keEa6CnOk= -github.com/spiffe/aws-spiffe-workload-helper v0.0.4/go.mod h1:off3o6L61qUpoyoHIBSC7W75EVzAwMIRr2Va6Oy9988= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -2223,8 +2211,6 @@ github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXG github.com/transparency-dev/tessera v1.0.0-rc3 h1:v385KqMekDUKI3ZVJHCHE5MAz8LBrWsEKa6OzYLrz0k= github.com/transparency-dev/tessera v1.0.0-rc3/go.mod h1:aaLlvG/sEPMzT96iIF4hua6Z9pLzkfDtkbaUAR4IL8I= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= -github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= From 115821ca41d1db05682e86719a672f7ec141c173 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Wed, 22 Oct 2025 16:07:55 -0400 Subject: [PATCH 05/16] godoc --- lib/tbot/config/systemd/template.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/tbot/config/systemd/template.go b/lib/tbot/config/systemd/template.go index a7539c2eaf338..1c4af6fb57c25 100644 --- a/lib/tbot/config/systemd/template.go +++ b/lib/tbot/config/systemd/template.go @@ -26,15 +26,25 @@ import ( var ( //go:embed systemd.tmpl templateData string - Template = template.Must(template.New("").Parse(templateData)) + + // Template is the systemd unit template for tbot.. + Template = template.Must(template.New("").Parse(templateData)) ) +// TemplateParams are the parameters for the systemd unit template. type TemplateParams struct { - UnitName string - User string - Group string - AnonymousTelemetry bool - ConfigPath string - TBotPath string + // UnitName is the name of the systemd unit. + UnitName string + // User is the user to run the service as. + User string + // Group is the group to run the service as. + Group string + // AnonymousTelemetry is whether to enable anonymous telemetry. + AnonymousTelemetry bool + // ConfigPath is the path to the tbot config file. + ConfigPath string + // TBotPath is the path to the tbot binary. + TBotPath string + // DiagSocketForUpdater is the path to the diag socket for the updater. DiagSocketForUpdater string } From 463308a6c89dd616f622833e98e1b6f09b5d2377 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 13:00:25 -0400 Subject: [PATCH 06/16] linting, typos --- lib/service/service.go | 3 ++- lib/service/signals.go | 4 ++-- lib/tbot/internal/diagnostics/service.go | 4 ++-- lib/tbot/tbot.go | 3 ++- lib/utils/{ => process}/pid.go | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) rename lib/utils/{ => process}/pid.go (99%) diff --git a/lib/service/service.go b/lib/service/service.go index 3043248630ef4..054ac47b6bf33 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -180,6 +180,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/cert" logutils "github.com/gravitational/teleport/lib/utils/log" + "github.com/gravitational/teleport/lib/utils/process" "github.com/gravitational/teleport/lib/versioncontrol/endpoint" uw "github.com/gravitational/teleport/lib/versioncontrol/upgradewindow" "github.com/gravitational/teleport/lib/web" @@ -1645,7 +1646,7 @@ func NewTeleport(cfg *servicecfg.Config) (_ *TeleportProcess, err error) { // create the new pid file only after started successfully if cfg.PIDFile != "" { - if err := utils.CreateLockedPIDFile(cfg.PIDFile); err != nil { + if err := process.CreateLockedPIDFile(cfg.PIDFile); err != nil { return nil, trace.Wrap(err, "creating pidfile") } } diff --git a/lib/service/signals.go b/lib/service/signals.go index 976e0a27beed8..dbfcf6a95a96d 100644 --- a/lib/service/signals.go +++ b/lib/service/signals.go @@ -294,7 +294,7 @@ func (process *TeleportProcess) createListener(typ ListenerType, address string) return nil, trace.BadParameter("listening is blocked") } - // When the process exists, the socket files are left behind (to cover + // When the process exits, the socket files are left behind (to cover // forking scenarios). To guarantee there won't be errors like "address // already in use", delete the file before starting the listener. if typ.Network() == "unix" { @@ -318,7 +318,7 @@ func (process *TeleportProcess) createListener(typ ListenerType, address string) // The default behavior for unix listeners is to delete the file when the // listener closes (unlinking). However, if the process forks, the file - // descriptor will be gone when its parent process exists, causing the new + // descriptor will be gone when its parent process exits, causing the new // listener to have no socket file. if unixListener, ok := listener.(*net.UnixListener); ok { unixListener.SetUnlinkOnClose(false) diff --git a/lib/tbot/internal/diagnostics/service.go b/lib/tbot/internal/diagnostics/service.go index 90bfe06226349..723327d4ff232 100644 --- a/lib/tbot/internal/diagnostics/service.go +++ b/lib/tbot/internal/diagnostics/service.go @@ -139,7 +139,7 @@ func (s *Service) Run(ctx context.Context) error { } }() - // When the process exists, the socket files are left behind (to cover + // When the process exits, the socket files are left behind (to cover // forking scenarios). To guarantee there won't be errors like "address // already in use", delete the file before starting the listener. if s.diagNetwork == "unix" { @@ -156,7 +156,7 @@ func (s *Service) Run(ctx context.Context) error { // The default behavior for unix listeners is to delete the file when the // listener closes (unlinking). However, if the process forks, the file - // descriptor will be gone when its parent process exists, causing the new + // descriptor will be gone when its parent process exits, causing the new // listener to have no socket file. if unixListener, ok := listener.(*net.UnixListener); ok { unixListener.SetUnlinkOnClose(false) diff --git a/lib/tbot/tbot.go b/lib/tbot/tbot.go index ea9378cda794b..f8f9a628a19d4 100644 --- a/lib/tbot/tbot.go +++ b/lib/tbot/tbot.go @@ -52,6 +52,7 @@ import ( workloadidentitysvc "github.com/gravitational/teleport/lib/tbot/services/workloadidentity" "github.com/gravitational/teleport/lib/tbot/workloadidentity" "github.com/gravitational/teleport/lib/utils" + "github.com/gravitational/teleport/lib/utils/process" ) var tracer = otel.Tracer("github.com/gravitational/teleport/lib/tbot") @@ -185,7 +186,7 @@ func (b *Bot) Run(ctx context.Context) (err error) { // create the new pid file only after started successfully if b.cfg.PIDFile != "" { - if err := utils.CreateLockedPIDFile(b.cfg.PIDFile); err != nil { + if err := process.CreateLockedPIDFile(b.cfg.PIDFile); err != nil { return trace.Wrap(err, "creating pidfile") } } diff --git a/lib/utils/pid.go b/lib/utils/process/pid.go similarity index 99% rename from lib/utils/pid.go rename to lib/utils/process/pid.go index 0c721f14c146b..9e859de45c593 100644 --- a/lib/utils/pid.go +++ b/lib/utils/process/pid.go @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package utils +package process import ( "fmt" From a9db4688e9ce6ff8be66ee0e7fa21407444faddc Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 13:26:49 -0400 Subject: [PATCH 07/16] lint --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 59419758e60db..9b36bfa77fcc1 100644 --- a/go.mod +++ b/go.mod @@ -215,7 +215,6 @@ require ( github.com/sigstore/sigstore v1.9.6-0.20250729224751-181c5d3339b3 github.com/sigstore/sigstore-go v1.1.2 github.com/sijms/go-ora/v2 v2.9.0 - github.com/sirupsen/logrus v1.9.3 github.com/snowflakedb/gosnowflake v1.17.0 github.com/spf13/cobra v1.10.1 github.com/spiffe/go-spiffe/v2 v2.6.0 @@ -565,6 +564,7 @@ require ( github.com/sigstore/rekor v1.4.2 // indirect github.com/sigstore/rekor-tiles v0.1.11 // indirect github.com/sigstore/timestamp-authority v1.2.9 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect From 9dce61596a22142911ba3b5cbc495fb7581cd71c Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 13:48:53 -0400 Subject: [PATCH 08/16] shared cli args + cli tests --- lib/tbot/cli/start_legacy_test.go | 4 ++++ lib/tbot/cli/start_shared.go | 11 +++++++++-- lib/tbot/cli/start_shared_test.go | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/tbot/cli/start_legacy_test.go b/lib/tbot/cli/start_legacy_test.go index a41020dbe5353..4456332d85d8f 100644 --- a/lib/tbot/cli/start_legacy_test.go +++ b/lib/tbot/cli/start_legacy_test.go @@ -48,6 +48,8 @@ func TestLegacyCommand(t *testing.T) { "--data-dir=/foo", "--destination-dir=/bar", "--auth-server=example.com:3024", + "--pid-file=/run/tbot.pid", + "--diag-socket-for-updater=/var/lib/teleport/bot/debug.sock", }, assertConfig: func(t *testing.T, cfg *config.BotConfig) { token, err := cfg.Onboarding.Token() @@ -61,6 +63,8 @@ func TestLegacyCommand(t *testing.T) { require.True(t, cfg.Oneshot) require.Equal(t, "0.0.0.0:8080", cfg.DiagAddr) require.Equal(t, "example.com:3024", cfg.AuthServer) + require.Equal(t, "/run/tbot.pid", cfg.PIDFile) + require.Equal(t, "/var/lib/teleport/bot/debug.sock", cfg.DiagSocketForUpdater) dir, ok := cfg.Storage.Destination.(*destination.Directory) require.True(t, ok) diff --git a/lib/tbot/cli/start_shared.go b/lib/tbot/cli/start_shared.go index 00e8f39bcb64c..493ce07da1beb 100644 --- a/lib/tbot/cli/start_shared.go +++ b/lib/tbot/cli/start_shared.go @@ -119,8 +119,10 @@ type sharedStartArgs struct { StaticKeyPath string Keypair string - Oneshot bool - DiagAddr string + Oneshot bool + DiagAddr string + DiagSocketForUpdater string + PIDFile string oneshotSetByUser bool } @@ -146,6 +148,8 @@ func newSharedStartArgs(cmd *kingpin.CmdClause) *sharedStartArgs { cmd.Flag("registration-secret-path", "For bound keypair joining, specifies a file containing a registration secret for use at first join.").StringVar(&args.RegistrationSecretPath) cmd.Flag("static-key-path", "For bound keypair joining, specifies a path to a static key.").StringVar(&args.StaticKeyPath) cmd.Flag("join-uri", "An optional URI with joining and authentication parameters. Individual flags for proxy, join method, token, etc may be used instead.").StringVar(&args.JoiningURI) + cmd.Flag("diag-socket-for-updater", "If set, run the diagnostics service on the specified socket path for teleport-update to consume.").Hidden().StringVar(&args.DiagSocketForUpdater) + cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created").StringVar(&args.PIDFile) return args } @@ -264,6 +268,9 @@ func (s *sharedStartArgs) ApplyConfig(cfg *config.BotConfig, l *slog.Logger) err cfg.Onboarding.BoundKeypair.StaticPrivateKeyPath = s.StaticKeyPath } + cfg.DiagSocketForUpdater = s.DiagSocketForUpdater + cfg.PIDFile = s.PIDFile + return nil } diff --git a/lib/tbot/cli/start_shared_test.go b/lib/tbot/cli/start_shared_test.go index e68e5991746b8..b4bf6bc6d3d03 100644 --- a/lib/tbot/cli/start_shared_test.go +++ b/lib/tbot/cli/start_shared_test.go @@ -44,6 +44,8 @@ func TestSharedStartArgs(t *testing.T) { "--diag-addr=0.0.0.0:8080", "--storage=file:///foo/bar", "--proxy-server=example.teleport.sh:443", + "--pid-file=/run/tbot.pid", + "--diag-socket-for-updater=/var/lib/teleport/bot/debug.sock", }) require.NoError(t, err) @@ -56,6 +58,8 @@ func TestSharedStartArgs(t *testing.T) { require.Equal(t, "0.0.0.0:8080", args.DiagAddr) require.Equal(t, "file:///foo/bar", args.Storage) require.Equal(t, "example.teleport.sh:443", args.ProxyServer) + require.Equal(t, "/run/tbot.pid", args.PIDFile) + require.Equal(t, "/var/lib/teleport/bot/debug.sock", args.DiagSocketForUpdater) // Convert these args to a BotConfig. cfg, err := LoadConfigWithMutators(&GlobalArgs{}, args) From c1976c2afe0a5a6774c1e3d6c46bf41a748ddef3 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 14:40:52 -0400 Subject: [PATCH 09/16] lint --- lib/service/service.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/service/service.go b/lib/service/service.go index 054ac47b6bf33..b90109ad52c6f 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -180,7 +180,7 @@ import ( "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/utils/cert" logutils "github.com/gravitational/teleport/lib/utils/log" - "github.com/gravitational/teleport/lib/utils/process" + procutils "github.com/gravitational/teleport/lib/utils/process" "github.com/gravitational/teleport/lib/versioncontrol/endpoint" uw "github.com/gravitational/teleport/lib/versioncontrol/upgradewindow" "github.com/gravitational/teleport/lib/web" @@ -1646,7 +1646,7 @@ func NewTeleport(cfg *servicecfg.Config) (_ *TeleportProcess, err error) { // create the new pid file only after started successfully if cfg.PIDFile != "" { - if err := process.CreateLockedPIDFile(cfg.PIDFile); err != nil { + if err := procutils.CreateLockedPIDFile(cfg.PIDFile); err != nil { return nil, trace.Wrap(err, "creating pidfile") } } From a58e19b3a812373b6c0eaca6ce5919e71d707591 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 14:48:31 -0400 Subject: [PATCH 10/16] missing . --- lib/tbot/cli/start_legacy.go | 2 +- lib/tbot/cli/start_shared.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tbot/cli/start_legacy.go b/lib/tbot/cli/start_legacy.go index 03322e784a9c7..abc4a3ee54417 100644 --- a/lib/tbot/cli/start_legacy.go +++ b/lib/tbot/cli/start_legacy.go @@ -166,7 +166,7 @@ func NewLegacyCommand(parentCmd *kingpin.CmdClause, action MutatorAction, mode C c.cmd.Flag("oneshot", "If set, quit after the first renewal.").IsSetByUser(&c.oneshotSetByUser).BoolVar(&c.Oneshot) c.cmd.Flag("diag-addr", "If set and the bot is in debug mode, a diagnostics service will listen on specified address.").StringVar(&c.DiagAddr) c.cmd.Flag("diag-socket-for-updater", "If set, run the diagnostics service on the specified socket path for teleport-update to consume.").Hidden().StringVar(&c.DiagSocketForUpdater) - c.cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created").StringVar(&c.PIDFile) + c.cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created.").StringVar(&c.PIDFile) return c } diff --git a/lib/tbot/cli/start_shared.go b/lib/tbot/cli/start_shared.go index 493ce07da1beb..d6c2ced21495b 100644 --- a/lib/tbot/cli/start_shared.go +++ b/lib/tbot/cli/start_shared.go @@ -149,7 +149,7 @@ func newSharedStartArgs(cmd *kingpin.CmdClause) *sharedStartArgs { cmd.Flag("static-key-path", "For bound keypair joining, specifies a path to a static key.").StringVar(&args.StaticKeyPath) cmd.Flag("join-uri", "An optional URI with joining and authentication parameters. Individual flags for proxy, join method, token, etc may be used instead.").StringVar(&args.JoiningURI) cmd.Flag("diag-socket-for-updater", "If set, run the diagnostics service on the specified socket path for teleport-update to consume.").Hidden().StringVar(&args.DiagSocketForUpdater) - cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created").StringVar(&args.PIDFile) + cmd.Flag("pid-file", "Full path to the PID file. By default no PID file will be created.").StringVar(&args.PIDFile) return args } From 6599791a105909588481af67f1aed8ae793d22cf Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 14:52:42 -0400 Subject: [PATCH 11/16] flag docs --- docs/pages/reference/cli/tbot.mdx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/reference/cli/tbot.mdx b/docs/pages/reference/cli/tbot.mdx index 0829763f4a13a..8f792588a6970 100644 --- a/docs/pages/reference/cli/tbot.mdx +++ b/docs/pages/reference/cli/tbot.mdx @@ -291,6 +291,7 @@ specific section for details when using a YAML config file or legacy output. | `--registration-secret` | An optional joining secret to use on first join with the `bound_keypair` join method. This can also be provided via the `TBOT_REGISTRATION_SECRET` environment variable. | | `--registration-secret-path` | An optional path to a file containing a joining secret to use on first join with the `bound_keypair` join method. | | `--static-key-path` | An optional path to a file containing a static private key for use with the `bound_keypair` join method. A base64-encoded key can also be provided via the `TBOT_BOUND_KEYPAIR_STATIC_KEY` environment variable. | +| `--pid-file` | Full path to the PID file. By default no PID file will be created. | ## tbot start legacy @@ -321,6 +322,7 @@ another dedicated mode instead. | `--join-method` | Method to use to join the cluster. Can be `token`, `azure`, `circleci`, `gcp`, `github`, `gitlab` or `iam`. | | `--oneshot` | If set, quit after the first renewal. | | `--log-format` | Controls the format of output logs. Can be `json` or `text`. Defaults to `text`. | +| `--pid-file` | Full path to the PID file. By default no PID file will be created. | ### Examples From 19d19db4b9419f272eb32407898230dc10436818 Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Thu, 23 Oct 2025 15:38:59 -0400 Subject: [PATCH 12/16] pid example --- .../machine-id/diagnostics-service.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/pages/reference/machine-workload-identity/machine-id/diagnostics-service.mdx b/docs/pages/reference/machine-workload-identity/machine-id/diagnostics-service.mdx index 1b828e19f373e..588eefb7fe0b1 100644 --- a/docs/pages/reference/machine-workload-identity/machine-id/diagnostics-service.mdx +++ b/docs/pages/reference/machine-workload-identity/machine-id/diagnostics-service.mdx @@ -72,7 +72,8 @@ Content-Type: application/json "status": "unhealthy", "reason": "access denied to perform action \"read\" on \"workload_identity\"" } - } + }, + "pid": 42344 } ``` From 0c78ed2d620865bca14090e8d520345de055eabd Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Tue, 28 Oct 2025 13:04:48 -0400 Subject: [PATCH 13/16] Apply suggestion from @strideynet Co-authored-by: Noah Stride --- lib/tbot/internal/diagnostics/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tbot/internal/diagnostics/service.go b/lib/tbot/internal/diagnostics/service.go index 723327d4ff232..2529a75965406 100644 --- a/lib/tbot/internal/diagnostics/service.go +++ b/lib/tbot/internal/diagnostics/service.go @@ -143,7 +143,7 @@ func (s *Service) Run(ctx context.Context) error { // forking scenarios). To guarantee there won't be errors like "address // already in use", delete the file before starting the listener. if s.diagNetwork == "unix" { - s.log.DebugContext(ctx, "Deleting socket file", "path", s.diagAddr) + s.log.DebugContext(ctx, "Cleaning up socket file", "path", s.diagAddr) if err := trace.ConvertSystemError(os.Remove(s.diagAddr)); err != nil && !trace.IsNotFound(err) { s.log.WarnContext(ctx, "Failed to cleanup existing socket file", "error", err) } From bb717f58d9a4ad91f1afd2267afcec4f1904ea5e Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Tue, 28 Oct 2025 17:22:14 -0400 Subject: [PATCH 14/16] windows fix --- lib/utils/process/pid.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utils/process/pid.go b/lib/utils/process/pid.go index 9e859de45c593..00b95ee310aa6 100644 --- a/lib/utils/process/pid.go +++ b/lib/utils/process/pid.go @@ -1,3 +1,5 @@ +//go:build !windows + /* * Teleport * Copyright (C) 2025 Gravitational, Inc. From 80cc6f5d474d48b8fa4d54743d042caf2a5b3d7f Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Tue, 28 Oct 2025 17:26:27 -0400 Subject: [PATCH 15/16] windows fix for merge queue --- lib/utils/process/pid_windows.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 lib/utils/process/pid_windows.go diff --git a/lib/utils/process/pid_windows.go b/lib/utils/process/pid_windows.go new file mode 100644 index 0000000000000..5f834bc4d24b1 --- /dev/null +++ b/lib/utils/process/pid_windows.go @@ -0,0 +1,27 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package process + +// CreateLockedPIDFile creates a PID file in the path specified by pidFile +// containing the current PID, atomically swapping it in the final place and +// leaving it with an exclusive advisory lock that will get released when the +// process ends, for the benefit of "pkill -L". +func CreateLockedPIDFile(pidFile string) error { + return errors.New("PID files are not supported on Windows") +} From 38cfa2e1b4f53efd608946262f8a418561ee65fb Mon Sep 17 00:00:00 2001 From: Stephen Levine Date: Tue, 28 Oct 2025 18:37:09 -0400 Subject: [PATCH 16/16] missing errors on windows --- lib/utils/process/pid_windows.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/utils/process/pid_windows.go b/lib/utils/process/pid_windows.go index 5f834bc4d24b1..caac52010a006 100644 --- a/lib/utils/process/pid_windows.go +++ b/lib/utils/process/pid_windows.go @@ -18,6 +18,8 @@ package process +import "errors" + // CreateLockedPIDFile creates a PID file in the path specified by pidFile // containing the current PID, atomically swapping it in the final place and // leaving it with an exclusive advisory lock that will get released when the