| // Copyright 2021 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package policyenforcer |
| |
| import ( |
| "errors" |
| |
| "policy-manager/pkg/devicepolicy" |
| "policy-manager/pkg/systemd" |
| |
| "policy-manager/protos" |
| |
| "github.com/golang/glog" |
| "github.com/golang/protobuf/proto" |
| ) |
| |
| // PolicyEnforcer implements the PolicyEnforcer interface. |
| type PolicyEnforcer struct { |
| // systemdClient is the interface used to call systemd API. |
| systemdClient systemd.SystemdClient |
| } |
| |
| const updateDisabledStrategy = "update_disabled" |
| |
| func startOrStopUnit(systemdClient systemd.SystemdClient, unitName string, isStartUnit bool) error { |
| if isStartUnit { |
| glog.Infof("Starting service: %v", unitName) |
| return systemdClient.StartUnit(unitName) |
| } else { |
| glog.Infof("Stopping service: %v", unitName) |
| return systemdClient.StopUnit(unitName) |
| } |
| } |
| |
| // NewPolicyEnforcer returns a new PolicyEnforcer for applying the |
| // the COS device policy and status check |
| func NewPolicyEnforcer(systemdClient systemd.SystemdClient) *PolicyEnforcer { |
| return &PolicyEnforcer{systemdClient} |
| } |
| |
| // UpdateServiceState reads device policy from disk, and then apply |
| // the configuration to the instance. If the desired state is to be running, |
| // the function will start or stop the services to apply the configuration |
| // in device policy. |
| func (client *PolicyEnforcer) UpdateServiceState(cosDevicePolicyFile string, serviceMonitor map[string]string) error { |
| // Reads the device policy files from disk to get desired state. |
| config, err := devicepolicy.GetInstanceConfig(cosDevicePolicyFile) |
| if err != nil { |
| return err |
| } |
| |
| // Gets the current status of services. |
| serviceStatus, err := client.GetServiceStatus(serviceMonitor) |
| |
| if err != nil { |
| return err |
| } |
| |
| updateStateErr := false |
| |
| // Start or stop the services to reach desired state. |
| if serviceStatus.GetMetrics() != config.GetMetricsEnabled() { |
| if err := startOrStopUnit(client.systemdClient, |
| serviceMonitor["metricsService"], config.GetMetricsEnabled()); err != nil { |
| glog.Error(err) |
| updateStateErr = true |
| } |
| } |
| |
| if serviceStatus.GetUpdateEngine() != config.GetUpdateStrategy() { |
| updateStrategyMode := true |
| if config.GetUpdateStrategy() == updateDisabledStrategy { |
| updateStrategyMode = false |
| } |
| |
| if err := startOrStopUnit(client.systemdClient, |
| serviceMonitor["updateService"], updateStrategyMode); err != nil { |
| glog.Error(err) |
| updateStateErr = true |
| } |
| } |
| |
| if serviceStatus.GetLogging() != config.HealthMonitorConfig.GetLoggingEnabled() { |
| if err := startOrStopUnit(client.systemdClient, |
| serviceMonitor["loggingService"], config.HealthMonitorConfig.GetLoggingEnabled()); err != nil { |
| glog.Error(err) |
| updateStateErr = true |
| } |
| } |
| |
| if serviceStatus.GetMonitoring() != config.HealthMonitorConfig.GetMonitoringEnabled() { |
| if err := startOrStopUnit(client.systemdClient, |
| serviceMonitor["monitoringService"], config.HealthMonitorConfig.GetMonitoringEnabled()); err != nil { |
| glog.Error(err) |
| updateStateErr = true |
| } |
| } |
| |
| if updateStateErr { |
| return errors.New("unable to apply health monitor policy") |
| } |
| return nil |
| } |
| |
| // GetServiceStatus checks whether the services are running and returns their |
| // status in a ServiceStatus proto. Failure to check one service will result |
| // in a missing field in the returned proto, and will not affect checking the |
| // other service's status. |
| func (client *PolicyEnforcer) GetServiceStatus(serviceMonitor map[string]string) (*protos.ServiceStatus, error) { |
| ServiceStatus := new(protos.ServiceStatus) |
| |
| statusErr := false |
| isRunning, err := client.systemdClient.IsUnitActiveRunning(serviceMonitor["metricsService"]) |
| if err != nil { |
| glog.Error(err) |
| statusErr = true |
| } else { |
| ServiceStatus.Metrics = proto.Bool(isRunning) |
| } |
| |
| isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["updateService"]) |
| if err != nil { |
| glog.Error(err) |
| statusErr = true |
| |
| } else { |
| if isRunning { |
| ServiceStatus.UpdateEngine = proto.String("") |
| } else { |
| ServiceStatus.UpdateEngine = proto.String(updateDisabledStrategy) |
| } |
| } |
| |
| isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["loggingService"]) |
| if err != nil { |
| glog.Error(err) |
| statusErr = true |
| } else { |
| ServiceStatus.Logging = proto.Bool(isRunning) |
| } |
| |
| isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["monitoringService"]) |
| if err != nil { |
| glog.Error(err) |
| statusErr = true |
| |
| } else { |
| ServiceStatus.Monitoring = proto.Bool(isRunning) |
| } |
| |
| if statusErr { |
| return ServiceStatus, errors.New("unable to get service status") |
| } |
| return ServiceStatus, nil |
| } |