sys-apps/policymanager: skip crash-reporter check if service not found

If crash-reporter.service is not installed on the system (such as on UVM
images), skip stopping it during device policy initialization, checking
its status, and enforcing policy on it instead of failing.

BUG=b/542235175
TEST=FEATURES="test" emerge sys-apps/policymanager
RELEASE_NOTE=None

Change-Id: If71b1fbf19a81a5c4111dd0961cb2e7a82bb9a29
diff --git a/cmd/monitor/monitor.go b/cmd/monitor/monitor.go
index e941abf..5c12c47 100644
--- a/cmd/monitor/monitor.go
+++ b/cmd/monitor/monitor.go
@@ -136,9 +136,18 @@
 	// is in `Activating` state due to which it is returning error and we don't
 	// need this service until cos-metrics-enabled is set to true.
 	systemdClient := systemd.NewSystemdClient(systemctlCmd)
-	if err := systemdClient.StopUnit(serviceMonitor["metricsService"]); err != nil {
+	metricsService := serviceMonitor["metricsService"]
+	metricsExists, err := systemdClient.ServiceExists(metricsService)
+	if err != nil {
 		return err
 	}
+	if metricsExists {
+		if err := systemdClient.StopUnit(metricsService); err != nil {
+			return err
+		}
+	} else {
+		glog.Infof("%s does not exist, skipping stopping service", metricsService)
+	}
 
 	return devicepolicy.InitDevicePolicy(initConfig, devicePolicyFile, instanceConfigFile, publicKeyFile, privateKeyFile)
 }
diff --git a/pkg/policyenforcer/policy_enforcer.go b/pkg/policyenforcer/policy_enforcer.go
index 14f6bc3..cd22326 100644
--- a/pkg/policyenforcer/policy_enforcer.go
+++ b/pkg/policyenforcer/policy_enforcer.go
@@ -72,7 +72,7 @@
 	updateStateErr := false
 
 	// Start or stop the services to reach desired state.
-	if serviceStatus.GetMetrics() != config.GetMetricsEnabled() {
+	if serviceStatus.Metrics != nil && serviceStatus.GetMetrics() != config.GetMetricsEnabled() {
 		if err := startOrStopUnit(client.systemdClient,
 			serviceMonitor["metricsService"], config.GetMetricsEnabled()); err != nil {
 			glog.Error(err)
@@ -123,12 +123,22 @@
 	ServiceStatus := new(protos.ServiceStatus)
 
 	statusErr := false
-	isRunning, err := client.systemdClient.IsUnitActiveRunning(serviceMonitor["metricsService"])
+	var isRunning bool
+	metricsService := serviceMonitor["metricsService"]
+	metricsExists, err := client.systemdClient.ServiceExists(metricsService)
 	if err != nil {
-		glog.Error(err)
+		glog.Errorf("failed to check if %s exists: %v", metricsService, err)
 		statusErr = true
+	} else if metricsExists {
+		isRunning, err = client.systemdClient.IsUnitActiveRunning(metricsService)
+		if err != nil {
+			glog.Error(err)
+			statusErr = true
+		} else {
+			ServiceStatus.Metrics = proto.Bool(isRunning)
+		}
 	} else {
-		ServiceStatus.Metrics = proto.Bool(isRunning)
+		glog.Infof("%s does not exist, skipping status check", metricsService)
 	}
 
 	updateService := serviceMonitor["updateService"]
diff --git a/pkg/policyenforcer/policy_enforcer_test.go b/pkg/policyenforcer/policy_enforcer_test.go
index d70bd00..263b634 100644
--- a/pkg/policyenforcer/policy_enforcer_test.go
+++ b/pkg/policyenforcer/policy_enforcer_test.go
@@ -55,6 +55,7 @@
 func TestUpdateServiceState(t *testing.T) {
 	tests := []struct {
 		name                          string
+		metricsNotExist               bool
 		updateNotExist                bool
 		onDiskConfig                  *protos.InstanceConfig
 		getConfigErr                  error
@@ -233,6 +234,24 @@
 			expectChangeMonitoringErr:     nil,
 			expectErr:                     false,
 		},
+		{
+			name:            "MetricsServiceNotExist",
+			metricsNotExist: true,
+			onDiskConfig: &protos.InstanceConfig{
+				MetricsEnabled: proto.Bool(true),
+			},
+			getConfigErr:                  nil,
+			getStatusErr:                  nil,
+			isMetricsEnabled:              false,
+			isUpdateEnabled:               false,
+			isLogging:                     true,
+			isMonitoring:                  true,
+			expectChangeMetricsErr:        nil,
+			expectChangeUpdateStrategyErr: nil,
+			expectChangeLoggingErr:        nil,
+			expectChangeMonitoringErr:     nil,
+			expectErr:                     false,
+		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
@@ -246,6 +265,10 @@
 			sysapi.AtomicWriteFile(tmpCosDevicePolicyFile, expectedCosDevicePolicyBytes, cosDevicePolicyFilePerm)
 			defer os.RemoveAll(tmpCosDevicePolicyFile)
 
+			metricsServiceName := "metrics.service"
+			if test.metricsNotExist {
+				metricsServiceName = "metrics-not-exist.service"
+			}
 			updateServiceName := "update.service"
 			if test.updateNotExist {
 				updateServiceName = "update-not-exist.service"
@@ -253,7 +276,7 @@
 			serviceMonitor := map[string]string{
 				"loggingService":    fmt.Sprintf("%s,%t,%v", "logging.service", test.isLogging, test.expectChangeLoggingErr),
 				"monitoringService": fmt.Sprintf("%s,%t,%v", "monitoring.service", test.isMonitoring, test.expectChangeMonitoringErr),
-				"metricsService":    fmt.Sprintf("%s,%t,%v", "metrics.service", test.isMetricsEnabled, test.expectChangeMetricsErr),
+				"metricsService":    fmt.Sprintf("%s,%t,%v", metricsServiceName, test.isMetricsEnabled, test.expectChangeMetricsErr),
 				"updateService":     fmt.Sprintf("%s,%t,%v", updateServiceName, test.isUpdateEnabled, test.expectChangeUpdateStrategyErr),
 			}
 
@@ -279,6 +302,7 @@
 func TestGetServiceStatus(t *testing.T) {
 	tests := []struct {
 		name                     string
+		metricsNotExist          bool
 		isMetricsEnabled         bool
 		checkMetricsErr          error
 		updateNotExist           bool
@@ -405,11 +429,30 @@
 			isMonitoring:             true,
 			checkMonitoringErr:       nil,
 			expectedStatus: &protos.ServiceStatus{
+				Metrics:    proto.Bool(false),
 				Logging:    proto.Bool(true),
 				Monitoring: proto.Bool(true),
 			},
 			expectErr: false,
 		},
+		{
+			name:                     "MetricsServiceNotExist",
+			metricsNotExist:          true,
+			isMetricsEnabled:         false,
+			checkMetricsErr:          nil,
+			isUpdateEnabled:          false,
+			checkUpdateDisabledError: nil,
+			isLogging:                true,
+			checkLoggingErr:          nil,
+			isMonitoring:             true,
+			checkMonitoringErr:       nil,
+			expectedStatus: &protos.ServiceStatus{
+				UpdateEngine: proto.String("update_disabled"),
+				Logging:      proto.Bool(true),
+				Monitoring:   proto.Bool(true),
+			},
+			expectErr: false,
+		},
 	}
 
 	for _, test := range tests {
@@ -417,6 +460,10 @@
 			var err error
 			fakeSystemdClient := systemd.NewSystemdClient(systemctlCmd)
 
+			metricsServiceName := "metrics.service"
+			if test.metricsNotExist {
+				metricsServiceName = "metrics-not-exist.service"
+			}
 			updateServiceName := "update.service"
 			if test.updateNotExist {
 				updateServiceName = "update-not-exist.service"
@@ -424,7 +471,7 @@
 			serviceMonitor := map[string]string{
 				"loggingService":    fmt.Sprintf("%s,%t,%v", "logging.service", test.isLogging, test.checkLoggingErr),
 				"monitoringService": fmt.Sprintf("%s,%t,%v", "monitoring.service", test.isMonitoring, test.checkMonitoringErr),
-				"metricsService":    fmt.Sprintf("%s,%t,%v", "metrics.service", test.isMetricsEnabled, test.checkMetricsErr),
+				"metricsService":    fmt.Sprintf("%s,%t,%v", metricsServiceName, test.isMetricsEnabled, test.checkMetricsErr),
 				"updateService":     fmt.Sprintf("%s,%t,%v", updateServiceName, test.isUpdateEnabled, test.checkUpdateDisabledError),
 			}