//UpdateInstanceInformation calls the UpdateInstanceInformation SSM API. func (svc *sdkService) UpdateInstanceInformation( log log.T, agentVersion string, agentStatus string, ) (response *ssm.UpdateInstanceInformationOutput, err error) { params := ssm.UpdateInstanceInformationInput{ AgentStatus: aws.String(agentStatus), AgentVersion: aws.String(agentVersion), } goOS := runtime.GOOS switch goOS { case "windows": params.PlatformType = aws.String(ssm.PlatformTypeWindows) case "linux": params.PlatformType = aws.String(ssm.PlatformTypeLinux) default: return nil, fmt.Errorf("Cannot report platform type of unrecognized OS. %v", goOS) } if ip, err := platform.IP(); err == nil { params.IPAddress = aws.String(ip) } else { log.Warn(err) } if h, err := platform.Hostname(); err == nil { params.ComputerName = aws.String(h) } else { log.Warn(err) } if instID, err := platform.InstanceID(); err == nil { params.InstanceId = aws.String(instID) } else { log.Warn(err) } if n, err := platform.PlatformName(log); err == nil { params.PlatformName = aws.String(n) } else { log.Warn(err) } if v, err := platform.PlatformVersion(log); err == nil { params.PlatformVersion = aws.String(v) } else { log.Warn(err) } log.Debug("Calling UpdateInstanceInformation with params", params) response, err = svc.sdk.UpdateInstanceInformation(¶ms) if err != nil { sdkutil.HandleAwsError(log, err, ssmStopPolicy) return } log.Debug("UpdateInstanceInformation Response", response) return }
// NewCoreManager creates a new core plugin manager. func NewCoreManager(instanceIdPtr *string, regionPtr *string, log logger.T) (cm *CoreManager, err error) { // initialize appconfig var config appconfig.SsmagentConfig if config, err = appconfig.Config(false); err != nil { log.Errorf("Could not load config file: %v", err) return } // initialize region if *regionPtr != "" { if err = platform.SetRegion(*regionPtr); err != nil { log.Errorf("error occured setting the region, %v", err) return } } var region string if region, err = platform.Region(); err != nil { log.Errorf("error fetching the region, %v", err) return } log.Debug("Using region:", region) // initialize instance ID if *instanceIdPtr != "" { if err = platform.SetInstanceID(*instanceIdPtr); err != nil { log.Errorf("error occured setting the instance ID, %v", err) return } } var instanceId string if instanceId, err = platform.InstanceID(); err != nil { log.Errorf("error fetching the instanceID, %v", err) return } log.Debug("Using instanceID:", instanceId) if err = fileutil.HardenDataFolder(); err != nil { log.Errorf("error initializing SSM data folder with hardened ACL, %v", err) return } //Initialize all folders where interim states of executing commands will be stored. if !initializeBookkeepingLocations(log, instanceId) { log.Error("unable to initialize. Exiting") return } context := context.Default(log, config).With("[instanceID=" + instanceId + "]") corePlugins := coreplugins.RegisteredCorePlugins(context) return &CoreManager{ context: context, corePlugins: *corePlugins, }, nil }
func isManagedInstance() (bool, error) { instanceId, err := platform.InstanceID() if err != nil { return false, err } if strings.Contains(instanceId, "mi-") { return true, nil } return false, nil }
// processOlderMessages processes older messages that have been persisted in various locations (like from pending & current folders) func (p *Processor) processOlderMessages() { log := p.context.Log() instanceID, err := platform.InstanceID() if err != nil { log.Errorf("error fetching instance id, %v", err) return } //process older messages from PENDING folder unprocessedMsgsLocation := path.Join(appconfig.DefaultDataStorePath, instanceID, appconfig.DefaultCommandRootDirName, appconfig.DefaultLocationOfState, appconfig.DefaultLocationOfPending) if isDirectoryEmpty, _ := fileutil.IsDirEmpty(unprocessedMsgsLocation); isDirectoryEmpty { log.Debugf("No messages to process from %v", unprocessedMsgsLocation) } else { //get all pending messages files, err := ioutil.ReadDir(unprocessedMsgsLocation) //TODO: revisit this when bookkeeping is made invasive if err != nil { log.Errorf("skipping reading pending messages from %v. unexpected error encountered - %v", unprocessedMsgsLocation, err) return } //iterate through all pending messages for _, f := range files { log.Debugf("Processing an older message with messageID - %v", f.Name()) //construct the absolute path - safely assuming that interim state for older messages are already present in Pending folder file := path.Join(appconfig.DefaultDataStorePath, instanceID, appconfig.DefaultCommandRootDirName, appconfig.DefaultLocationOfState, appconfig.DefaultLocationOfPending, f.Name()) var msg ssmmds.Message //parse the message if err := jsonutil.UnmarshalFile(file, &msg); err != nil { log.Errorf("skipping processsing of pending messages. encountered error %v while reading pending message from file - %v", err, f) } else { //call processMessage for every message read from the pending folder //we will end up sending acks again for this message - but that's ok because //unless a message has been deleted - multiple acks are allowed by MDS. //If this becomes an issue - we can stub out ack part from processMessage p.processMessage(&msg) } } } //process older messages from CURRENT folder p.processMessagesFromCurrent(instanceID) return }
// NewProcessor initializes a new mds processor with the given parameters. func NewProcessor(context context.T) *Processor { messageContext := context.With("[" + name + "]") log := messageContext.Log() config := messageContext.AppConfig() instanceID, err := platform.InstanceID() if instanceID == "" { log.Errorf("no instanceID provided, %v", err) return nil } mdsService := newMdsService(config) agentInfo := contracts.AgentInfo{ Lang: config.Os.Lang, Name: config.Agent.Name, Version: config.Agent.Version, Os: config.Os.Name, OsVersion: config.Os.Version, } agentConfig := contracts.AgentConfiguration{ AgentInfo: agentInfo, InstanceID: instanceID, } // sendCommand and cancelCommand will be processed by separate worker pools // so we can define the number of workers per each cancelWaitDuration := 10000 * time.Millisecond clock := times.DefaultClock sendCommandTaskPool := task.NewPool(log, config.Mds.CommandWorkersLimit, cancelWaitDuration, clock) cancelCommandTaskPool := task.NewPool(log, CancelWorkersLimit, cancelWaitDuration, clock) // create new message processor orchestrationRootDir := path.Join(appconfig.DefaultDataStorePath, instanceID, appconfig.DefaultCommandRootDirName, config.Agent.OrchestrationRootDir) replyBuilder := func(pluginID string, results map[string]*contracts.PluginResult) messageContracts.SendReplyPayload { runtimeStatuses := parser.PrepareRuntimeStatuses(log, results) return parser.PrepareReplyPayload(pluginID, runtimeStatuses, clock.Now(), agentConfig.AgentInfo) } statusReplyBuilder := func(agentInfo contracts.AgentInfo, resultStatus contracts.ResultStatus, documentTraceOutput string) messageContracts.SendReplyPayload { return parser.PrepareReplyPayloadToUpdateDocumentStatus(agentInfo, resultStatus, documentTraceOutput) } // create a stop policy where we will stop after 10 consecutive errors and if time period expires. processorStopPolicy := newStopPolicy() // SendResponse is used to send response on plugin completion. // If pluginID is empty it will send responses of all plugins. // If pluginID is specified, response will be sent of that particular plugin. sendResponse := func(messageID string, pluginID string, results map[string]*contracts.PluginResult) { payloadDoc := replyBuilder(pluginID, results) processSendReply(log, messageID, mdsService, payloadDoc, processorStopPolicy) } // SendDocLevelResponse is used to send document level update // Specify a new status of the document sendDocLevelResponse := func(messageID string, resultStatus contracts.ResultStatus, documentTraceOutput string) { payloadDoc := statusReplyBuilder(agentInfo, resultStatus, documentTraceOutput) processSendReply(log, messageID, mdsService, payloadDoc, processorStopPolicy) } // PersistData is used to persist the data into a bookkeeping folder persistData := func(msg *ssmmds.Message, bookkeeping string) { commandStateHelper.PersistData(log, getCommandID(*msg.MessageId), *msg.Destination, bookkeeping, *msg) } return &Processor{ context: messageContext, stopSignal: make(chan bool), config: agentConfig, service: mdsService, pluginRunner: pluginRunner, sendCommandPool: sendCommandTaskPool, cancelCommandPool: cancelCommandTaskPool, buildReply: replyBuilder, sendResponse: sendResponse, sendDocLevelResponse: sendDocLevelResponse, orchestrationRootDir: orchestrationRootDir, persistData: persistData, processorStopPolicy: processorStopPolicy, } }