// prepareRuntimeStatus creates the structure for the runtimeStatus section of the payload of SendReply // for a particular plugin. func prepareRuntimeStatus(log log.T, pluginResult contracts.PluginResult) contracts.PluginRuntimeStatus { var resultAsString string if err := pluginResult.Error; err == nil { resultAsString = fmt.Sprintf("%v", pluginResult.Output) } else { resultAsString = err.Error() } runtimeStatus := contracts.PluginRuntimeStatus{ Code: pluginResult.Code, Status: pluginResult.Status, Output: resultAsString, StartDateTime: times.ToIso8601UTC(pluginResult.StartDateTime), EndDateTime: times.ToIso8601UTC(pluginResult.EndDateTime), } if pluginResult.OutputS3BucketName != "" { runtimeStatus.OutputS3BucketName = pluginResult.OutputS3BucketName if pluginResult.OutputS3KeyPrefix != "" { runtimeStatus.OutputS3KeyPrefix = pluginResult.OutputS3KeyPrefix } } if runtimeStatus.Status == contracts.ResultStatusFailed && runtimeStatus.Code == 0 { runtimeStatus.Code = 1 } return runtimeStatus }
// PrepareReplyPayload creates the payload object for SendReply based on plugin outputs. func PrepareReplyPayload(pluginID string, runtimeStatuses map[string]*contracts.PluginRuntimeStatus, dateTime time.Time, agentInfo contracts.AgentInfo) (payload messageContracts.SendReplyPayload) { // TODO instance this needs to be revised to be in parity with ec2config documentStatus := contracts.ResultStatusSuccess var runtimeStatusCounts = map[string]int{} pluginCounts := len(runtimeStatuses) for _, pluginResult := range runtimeStatuses { if pluginResult.Status == contracts.ResultStatusFailed { documentStatus = contracts.ResultStatusFailed } runtimeStatusCounts[string(pluginResult.Status)]++ } // New precedence order of plugin states // Failed > TimedOut > Cancelled > Success > Cancelling > InProgress > Pending // The above order is a contract between SSM service and agent and hence for the calculation of aggregate // status of a (command) document, we follow the above precedence order. // // Note: // A command could have been failed/cancelled even before a plugin started executing, during which pendingItems > 0 // but overallResult.Status would be Failed/Cancelled. That's the reason we check for OverallResult status along // with number of failed/cancelled items. // TODO : We need to handle above to be able to send document traceoutput in case of document level errors. if runtimeStatusCounts[string(contracts.ResultStatusFailed)] > 0 { documentStatus = contracts.ResultStatusFailed } else if runtimeStatusCounts[string(contracts.ResultStatusTimedOut)] > 0 { documentStatus = contracts.ResultStatusTimedOut } else if runtimeStatusCounts[string(contracts.ResultStatusCancelled)] > 0 { documentStatus = contracts.ResultStatusCancelled } else if runtimeStatusCounts[string(contracts.ResultStatusSuccessAndReboot)] > 0 { documentStatus = contracts.ResultStatusSuccessAndReboot } else if runtimeStatusCounts[string(contracts.ResultStatusSuccess)] == pluginCounts { documentStatus = contracts.ResultStatusSuccess } else { documentStatus = contracts.ResultStatusInProgress } runtimeStatusesFiltered := make(map[string]*contracts.PluginRuntimeStatus) if pluginID != "" { runtimeStatusesFiltered[pluginID] = runtimeStatuses[pluginID] } else { runtimeStatusesFiltered = runtimeStatuses } payload = messageContracts.SendReplyPayload{ AdditionalInfo: contracts.AdditionalInfo{ Agent: agentInfo, DateTime: times.ToIso8601UTC(dateTime), RuntimeStatusCounts: runtimeStatusCounts, }, DocumentStatus: documentStatus, DocumentTraceOutput: "", // TODO: Fill me appropriately RuntimeStatus: runtimeStatusesFiltered, } return }
// prepareRuntimeStatus creates the structure for the runtimeStatus section of the payload of SendReply // for a particular plugin. func prepareRuntimeStatus(update *UpdateDetail) contracts.PluginRuntimeStatus { // Set default as failed, this will help us catch issues more proactively pluginStatus := update.Result code := 0 if pluginStatus == contracts.ResultStatusFailed { code = 1 } output := contracts.TruncateOutput(update.StandardOut, update.StandardError, contracts.MaximumPluginOutputSize) return contracts.PluginRuntimeStatus{ Code: code, Status: pluginStatus, Output: output, OutputS3BucketName: update.OutputS3BucketName, OutputS3KeyPrefix: update.OutputS3KeyPrefix, StartDateTime: times.ToIso8601UTC(update.StartDateTime), EndDateTime: times.ToIso8601UTC(time.Now()), } }
func createMDSMessage(commandID string, payload string, topic string, instanceID string) ssmmds.Message { messageCreatedDate := time.Date(2015, 7, 9, 23, 22, 39, 19000000, time.UTC) c := sha256.New() c.Write([]byte(payload)) payloadDigest := string(c.Sum(nil)) return ssmmds.Message{ CreatedDate: aws.String(times.ToIso8601UTC(messageCreatedDate)), Destination: aws.String(instanceID), MessageId: aws.String("aws.ssm." + commandID + "." + instanceID), Payload: aws.String(payload), PayloadDigest: aws.String(payloadDigest), Topic: aws.String(topic), } }
// prepareReplyPayload setups the reply payload func prepareReplyPayload(config appconfig.SsmagentConfig, update *UpdateDetail) (payload *messageContracts.SendReplyPayload) { runtimeStatuses := make(map[string]*contracts.PluginRuntimeStatus) rs := prepareRuntimeStatus(update) runtimeStatuses[appconfig.PluginNameAwsAgentUpdate] = &rs agentInfo := contracts.AgentInfo{ Lang: config.Os.Lang, Name: config.Agent.Name, Version: config.Agent.Version, Os: config.Os.Name, OsVersion: config.Os.Version, } payload = &messageContracts.SendReplyPayload{ AdditionalInfo: contracts.AdditionalInfo{ Agent: agentInfo, DateTime: times.ToIso8601UTC(time.Now()), }, DocumentStatus: rs.Status, DocumentTraceOutput: "", RuntimeStatus: runtimeStatuses, } return payload }