// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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 plugin contains all essential structs/interfaces for long running plugins package plugin import ( "path/filepath" "time" "github.com/aws/amazon-ssm-agent/agent/appconfig" "github.com/aws/amazon-ssm-agent/agent/context" "github.com/aws/amazon-ssm-agent/agent/fileutil" "github.com/aws/amazon-ssm-agent/agent/framework/processor/executer/iohandler" "github.com/aws/amazon-ssm-agent/agent/jsonutil" "github.com/aws/amazon-ssm-agent/agent/longrunning/plugin/rundaemon" "github.com/aws/amazon-ssm-agent/agent/task" ) // PluginState reflects state of a long running plugin type PluginState struct { LastConfigurationModifiedTime time.Time IsEnabled bool } // PluginInfo reflects information about long running plugins // This is also used by lrpm manager to persisting information & then later use it for reference type PluginInfo struct { Name string Configuration string State PluginState } // Plugin reflects a long running plugin type Plugin struct { Info PluginInfo Handler LongRunningPlugin } // LongRunningPlugin is the interface that must be implemented by all long running plugins type LongRunningPlugin interface { IsRunning() bool Start(configuration string, orchestrationDir string, cancelFlag task.CancelFlag, out iohandler.IOHandler) error Stop(cancelFlag task.CancelFlag) error } // PluginSettings reflects settings that can be applied to long running plugins like aws:cloudWatch type PluginSettings struct { StartType string } // LongRunningPluginInput represents input for long running plugin like aws:cloudWatch type LongRunningPluginInput struct { Settings PluginSettings Properties string } // RegisteredPlugins loads all long running plugins in memory func RegisteredPlugins(context context.T) map[string]Plugin { longrunningplugins := make(map[string]Plugin) log := context.Log() log.Debug("Registering long-running plugins") for key, value := range loadPlatformDependentPlugins(context) { log.Debugf("Adding platform-specific long-running plugin for %v", key) longrunningplugins[key] = value } for key, value := range loadPlatformIndependentPlugins(context) { if _, exists := longrunningplugins[key]; exists { log.Errorf("Duplicate long-running plugin - %v already registered", key) continue } context.Log().Debugf("Adding long-running plugin for %v", key) longrunningplugins[key] = value } context.Log().Debugf("Registered %v long-running plugins", len(longrunningplugins)) return longrunningplugins } // loadPlatformIndependentPlugins loads all long running plugins that don't have platform specific implementations func loadPlatformIndependentPlugins(context context.T) map[string]Plugin { //long running plugins that can be started/stopped/configured by long running plugin manager longrunningplugins := make(map[string]Plugin) for key, value := range loadDaemonPlugins(context) { context.Log().Debugf("Adding long-running plugin for %v", key) longrunningplugins[key] = value } return longrunningplugins } // loadDaemonPlugins registers long running plugin handlers for ssm daemons func loadDaemonPlugins(context context.T) map[string]Plugin { //long running daemon plugins that can be started/stopped/removed/configured by long running plugin manager daemonPlugins := make(map[string]Plugin) log := context.Log() // find all packages that should run as daemons and register a rundaemon plugin for each if err := fileutil.MakeDirs(appconfig.DaemonRoot); err != nil { log.Errorf("Unable to create ssm daemon folder %v: %v", appconfig.DaemonRoot, err) return daemonPlugins } daemons, err := fileutil.GetFileNames(appconfig.DaemonRoot) if err != nil { log.Errorf("error listing registered ssm daemons: %v", err) return daemonPlugins } for _, daemonFile := range daemons { // load file daemonFilePath := filepath.Join(appconfig.DaemonRoot, daemonFile) var input rundaemon.ConfigureDaemonPluginInput if err = jsonutil.UnmarshalFile(daemonFilePath, &input); err != nil { log.Errorf("Error unmarshalling %v, %v", daemonFilePath, err.Error()) } else { if err := rundaemon.ValidateDaemonInput(input); err != nil { log.Errorf("ssm daemon file %v is invalid: %v", daemonFilePath, err.Error()) } else { log.Infof("Registering long-running plugin for ssm daemon %v", input.Name) plugin := Plugin{ Info: PluginInfo{ Name: input.Name, Configuration: input.Command, State: PluginState{IsEnabled: true}, }, Handler: &rundaemon.Plugin{ Context: context, ExeLocation: input.PackageLocation, Name: input.Name, CommandLine: input.Command, }, } if _, exists := daemonPlugins[input.Name]; exists { log.Errorf("duplicate registrations exist for ssm daemon %v", input.Name) continue } daemonPlugins[input.Name] = plugin } } } return daemonPlugins }