blob: 51c03e142b97c1cb229658e3c9633c099ab2f768 [file] [log] [blame]
// 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 InstanceStatus 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.InstanceStatus, error) {
instanceStatus := new(protos.InstanceStatus)
statusErr := false
isRunning, err := client.systemdClient.IsUnitActiveRunning(serviceMonitor["metricsService"])
if err != nil {
glog.Error(err)
statusErr = true
} else {
instanceStatus.Metrics = proto.Bool(isRunning)
}
isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["updateService"])
if err != nil {
glog.Error(err)
statusErr = true
} else {
if isRunning {
instanceStatus.UpdateEngine = proto.String("")
} else {
instanceStatus.UpdateEngine = proto.String(updateDisabledStrategy)
}
}
isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["loggingService"])
if err != nil {
glog.Error(err)
statusErr = true
} else {
instanceStatus.Logging = proto.Bool(isRunning)
}
isRunning, err = client.systemdClient.IsUnitActiveRunning(serviceMonitor["monitoringService"])
if err != nil {
glog.Error(err)
statusErr = true
} else {
instanceStatus.Monitoring = proto.Bool(isRunning)
}
if statusErr {
return instanceStatus, errors.New("unable to get service status")
}
return instanceStatus, nil
}