From 3f80e80823c4e543841135626166f17bb4aae73e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:26:24 +0000 Subject: [PATCH 1/4] Initial plan From 618e1d89e6b57242eafb14ea402340ec3f87ef73 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:30:06 +0000 Subject: [PATCH 2/4] Handle Arc gcad service rename --- pkg/arc/consts.go | 12 +++++- pkg/arc/helpers.go | 25 ++++++++++-- pkg/arc/helpers_test.go | 86 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 pkg/arc/helpers_test.go diff --git a/pkg/arc/consts.go b/pkg/arc/consts.go index 0ad71bd3..d2766e11 100644 --- a/pkg/arc/consts.go +++ b/pkg/arc/consts.go @@ -19,7 +19,15 @@ var ( } // Arc services that may be present (not all are guaranteed to exist on every installation) - arcServices = []string{"himdsd", "gcarcservice", "extd"} + arcServices = []string{"himdsd", "gcarcservice", "gcad", "extd"} + + // Arc service groups required for installation verification. At least one + // service in each group must be active. + arcRequiredServiceGroups = [][]string{ + {"himdsd"}, + {"gcarcservice", "gcad"}, + {"extd"}, + } // Arc agent binary paths arcBinaryPaths = []string{ @@ -42,7 +50,9 @@ var ( arcServiceFiles = []string{ "/lib/systemd/system/himdsd.service", "/lib/systemd/system/gcarcservice.service", + "/lib/systemd/system/gcad.service", "/etc/systemd/system/himdsd.service", "/etc/systemd/system/gcarcservice.service", + "/etc/systemd/system/gcad.service", } ) diff --git a/pkg/arc/helpers.go b/pkg/arc/helpers.go index 839900d4..5440d20f 100644 --- a/pkg/arc/helpers.go +++ b/pkg/arc/helpers.go @@ -28,12 +28,31 @@ func isArcServicesRunning(ctx context.Context, logger *slog.Logger) bool { if !isArcAgentInstalled() { return false } - for _, service := range arcServices { - if !utilexec.IsServiceActive(ctx, logger, service) { + if !areArcServiceGroupsActive(ctx, logger, arcRequiredServiceGroups, utilexec.IsServiceActive) { + return false + } + return utilexec.RunCmdAt(ctx, logger, slog.LevelDebug, utilexec.Pgrep(), "-f", "azcmagent") == nil +} + +func areArcServiceGroupsActive( + ctx context.Context, + logger *slog.Logger, + serviceGroups [][]string, + isServiceActive func(context.Context, *slog.Logger, string) bool, +) bool { + for _, serviceGroup := range serviceGroups { + serviceGroupActive := false + for _, service := range serviceGroup { + if isServiceActive(ctx, logger, service) { + serviceGroupActive = true + break + } + } + if !serviceGroupActive { return false } } - return utilexec.RunCmdAt(ctx, logger, slog.LevelDebug, utilexec.Pgrep(), "-f", "azcmagent") == nil + return true } func ptrDeref[T any](p *T) T { diff --git a/pkg/arc/helpers_test.go b/pkg/arc/helpers_test.go new file mode 100644 index 00000000..c2d8d7de --- /dev/null +++ b/pkg/arc/helpers_test.go @@ -0,0 +1,86 @@ +package arc + +import ( + "context" + "io" + "log/slog" + "testing" +) + +func TestAreArcServiceGroupsActiveAcceptsLegacyOrRenamedGuestConfigService(t *testing.T) { + ctx := context.Background() + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + + tests := []struct { + name string + activeServices map[string]bool + want bool + }{ + { + name: "legacy service active", + activeServices: map[string]bool{ + "himdsd": true, + "gcarcservice": true, + "extd": true, + }, + want: true, + }, + { + name: "renamed service active", + activeServices: map[string]bool{ + "himdsd": true, + "gcad": true, + "extd": true, + }, + want: true, + }, + { + name: "neither service active", + activeServices: map[string]bool{ + "himdsd": true, + "extd": true, + }, + want: false, + }, + { + name: "required service inactive", + activeServices: map[string]bool{ + "gcad": true, + "extd": true, + }, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := areArcServiceGroupsActive(ctx, logger, arcRequiredServiceGroups, func(_ context.Context, _ *slog.Logger, service string) bool { + return tt.activeServices[service] + }) + if got != tt.want { + t.Fatalf("areArcServiceGroupsActive() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestArcServiceConstantsIncludeRenamedService(t *testing.T) { + if !stringSliceContains(arcServices, "gcad") { + t.Fatalf("arcServices must include gcad for azcmagent v1.62+ cleanup") + } + if !stringSliceContains(arcServiceFiles, "/lib/systemd/system/gcad.service") { + t.Fatalf("arcServiceFiles must include /lib/systemd/system/gcad.service") + } + if !stringSliceContains(arcServiceFiles, "/etc/systemd/system/gcad.service") { + t.Fatalf("arcServiceFiles must include /etc/systemd/system/gcad.service") + } +} + +func stringSliceContains(values []string, want string) bool { + for _, value := range values { + if value == want { + return true + } + } + return false +} From c86faadf9796a6bb6b4e624677339838376fce05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:36:38 +0000 Subject: [PATCH 3/4] Address Arc test review feedback --- pkg/arc/helpers_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/arc/helpers_test.go b/pkg/arc/helpers_test.go index c2d8d7de..c70903fc 100644 --- a/pkg/arc/helpers_test.go +++ b/pkg/arc/helpers_test.go @@ -7,7 +7,9 @@ import ( "testing" ) -func TestAreArcServiceGroupsActiveAcceptsLegacyOrRenamedGuestConfigService(t *testing.T) { +func TestAreArcServiceGroupsActive(t *testing.T) { + t.Parallel() + ctx := context.Background() logger := slog.New(slog.NewTextHandler(io.Discard, nil)) @@ -53,7 +55,10 @@ func TestAreArcServiceGroupsActiveAcceptsLegacyOrRenamedGuestConfigService(t *te } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + got := areArcServiceGroupsActive(ctx, logger, arcRequiredServiceGroups, func(_ context.Context, _ *slog.Logger, service string) bool { return tt.activeServices[service] }) @@ -65,6 +70,8 @@ func TestAreArcServiceGroupsActiveAcceptsLegacyOrRenamedGuestConfigService(t *te } func TestArcServiceConstantsIncludeRenamedService(t *testing.T) { + t.Parallel() + if !stringSliceContains(arcServices, "gcad") { t.Fatalf("arcServices must include gcad for azcmagent v1.62+ cleanup") } From 0346ef92ed03731da9ad7bd4dee25e385986db01 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 18:37:41 +0000 Subject: [PATCH 4/4] Refine Arc service tests --- pkg/arc/helpers_test.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/pkg/arc/helpers_test.go b/pkg/arc/helpers_test.go index c70903fc..82d49a23 100644 --- a/pkg/arc/helpers_test.go +++ b/pkg/arc/helpers_test.go @@ -4,6 +4,7 @@ import ( "context" "io" "log/slog" + "slices" "testing" ) @@ -72,22 +73,15 @@ func TestAreArcServiceGroupsActive(t *testing.T) { func TestArcServiceConstantsIncludeRenamedService(t *testing.T) { t.Parallel() - if !stringSliceContains(arcServices, "gcad") { + const renamedService = "gcad" + + if !slices.Contains(arcServices, renamedService) { t.Fatalf("arcServices must include gcad for azcmagent v1.62+ cleanup") } - if !stringSliceContains(arcServiceFiles, "/lib/systemd/system/gcad.service") { + if !slices.Contains(arcServiceFiles, "/lib/systemd/system/"+renamedService+".service") { t.Fatalf("arcServiceFiles must include /lib/systemd/system/gcad.service") } - if !stringSliceContains(arcServiceFiles, "/etc/systemd/system/gcad.service") { + if !slices.Contains(arcServiceFiles, "/etc/systemd/system/"+renamedService+".service") { t.Fatalf("arcServiceFiles must include /etc/systemd/system/gcad.service") } } - -func stringSliceContains(values []string, want string) bool { - for _, value := range values { - if value == want { - return true - } - } - return false -}