Ejemplo n.º 1
0
func generateTestCaseFromFiles(t *testing.T, messagePayloadFile string, messageReplyPayloadFile string, instanceID string) (testCase TestCaseSendCommand) {
	// load message payload and create MDS message from it
	payload, err := parser.ParseMessageWithParams(logger, string(loadFile(t, messagePayloadFile)))
	if err != nil {
		t.Fatal(err)
	}
	msgContent, err := jsonutil.Marshal(payload)
	if err != nil {
		t.Fatal(err)
	}
	testCase.Msg = createMDSMessage(payload.CommandID, msgContent, "aws.ssm.sendCommand.us.east.1.1", instanceID)
	testCase.MsgPayload = payload
	s3KeyPrefix := path.Join(payload.OutputS3KeyPrefix, payload.CommandID, *testCase.Msg.Destination)

	//orchestrationRootDir is set to CommandID considering that orchestration root directory name will be empty in the test case.
	orchestrationRootDir := getCommandID(*testCase.Msg.MessageId)

	testCase.PluginConfigs = getPluginConfigurations(payload.DocumentContent.RuntimeConfig,
		orchestrationRootDir,
		payload.OutputS3BucketName,
		s3KeyPrefix,
		*testCase.Msg.MessageId)

	testCase.PluginResults = make(map[string]*contracts.PluginResult)
	testCase.ReplyPayload = loadMessageReplyFromFile(t, messageReplyPayloadFile)
	for pluginName, pluginRuntimeStatus := range testCase.ReplyPayload.RuntimeStatus {
		pluginResult := parsePluginResult(t, *pluginRuntimeStatus)
		testCase.PluginResults[pluginName] = &pluginResult
	}
	return
}
Ejemplo n.º 2
0
// processSendCommandMessage processes a single send command message received from MDS.
func (p *Processor) processSendCommandMessage(context context.T,
	mdsService service.Service,
	messagesOrchestrationRootDir string,
	runPlugins PluginRunner,
	cancelFlag task.CancelFlag,
	buildReply replyBuilder,
	sendResponse engine.SendResponse,
	msg ssmmds.Message) {

	commandID := getCommandID(*msg.MessageId)

	log := context.Log()
	log.Debug("Processing send command message ", *msg.MessageId)
	log.Trace("Processing send command message ", jsonutil.Indent(*msg.Payload))

	parsedMessage, err := parser.ParseMessageWithParams(log, *msg.Payload)
	if err != nil {
		log.Error("format of received message is invalid ", err)
		err = mdsService.FailMessage(log, *msg.MessageId, service.InternalHandlerException)
		if err != nil {
			sdkutil.HandleAwsError(log, err, p.processorStopPolicy)
		}
		return
	}

	parsedMessageContent, _ := jsonutil.Marshal(parsedMessage)
	log.Debug("ParsedMessage is ", jsonutil.Indent(parsedMessageContent))

	// adapt plugin configuration format from MDS to plugin expected format
	s3KeyPrefix := path.Join(parsedMessage.OutputS3KeyPrefix, parsedMessage.CommandID, *msg.Destination)

	messageOrchestrationDirectory := filepath.Join(messagesOrchestrationRootDir, commandID)

	// Check if it is a managed instance and its executing managed instance incompatible AWS SSM public document.
	// A few public AWS SSM documents contain code which is not compatible when run on managed instances.
	// isManagedInstanceIncompatibleAWSSSMDocument makes sure to find such documents at runtime and replace the incompatible code.
	isMI, err := isManagedInstance()
	if err != nil {
		log.Errorf("Error determining managed instance. error: %v", err)
	}

	if isMI && isManagedInstanceIncompatibleAWSSSMDocument(parsedMessage.DocumentName) {
		log.Debugf("Running Incompatible AWS SSM Document %v on managed instance", parsedMessage.DocumentName)
		if parsedMessage, err = removeDependencyOnInstanceMetadataForManagedInstance(context, parsedMessage); err != nil {
			return
		}
	}

	pluginConfigurations := getPluginConfigurations(
		parsedMessage.DocumentContent.RuntimeConfig,
		messageOrchestrationDirectory,
		parsedMessage.OutputS3BucketName,
		s3KeyPrefix,
		*msg.MessageId)

	//persist : all information in current folder
	log.Info("Persisting message in current execution folder")

	//Data format persisted in Current Folder is defined by the struct - CommandState
	interimCmdState := initializeCommandState(pluginConfigurations, msg, parsedMessage)

	// persist new interim command state in current folder
	commandStateHelper.PersistData(log, commandID, *msg.Destination, appconfig.DefaultLocationOfCurrent, interimCmdState)

	//Deleting from pending folder since the command is getting executed
	commandStateHelper.RemoveData(log, commandID, *msg.Destination, appconfig.DefaultLocationOfPending)

	log.Debug("Running plugins...")
	outputs := runPlugins(context, *msg.MessageId, pluginConfigurations, sendResponse, cancelFlag)
	pluginOutputContent, _ := jsonutil.Marshal(outputs)
	log.Debugf("Plugin outputs %v", jsonutil.Indent(pluginOutputContent))

	payloadDoc := buildReply("", outputs)

	//check if document isn't supported by SSM -> update the DocumentLevel status message & send reply accordingly
	ssmDocName := parsedMessage.DocumentName
	if IsDocumentNotSupportedBySsmAgent(ssmDocName) {
		log.Infof("%s is not yet supported by aws-ssm-agent, setting up Document level response accordingly", ssmDocName)
		payloadDoc.DocumentTraceOutput = fmt.Sprintf("%s document is not yet supported by amazon-ssm-agent.", ssmDocName)
		p.sendDocLevelResponse(*msg.MessageId, contracts.ResultStatusFailed, payloadDoc.DocumentTraceOutput)
	}

	//update documentInfo in interim cmd state file
	documentInfo := commandStateHelper.GetDocumentInfo(log, commandID, *msg.Destination, appconfig.DefaultLocationOfCurrent)

	// set document level information which wasn't set previously
	documentInfo.AdditionalInfo = payloadDoc.AdditionalInfo
	documentInfo.DocumentStatus = payloadDoc.DocumentStatus
	documentInfo.DocumentTraceOutput = payloadDoc.DocumentTraceOutput
	documentInfo.RuntimeStatus = payloadDoc.RuntimeStatus

	//persist final documentInfo.
	commandStateHelper.PersistDocumentInfo(log,
		documentInfo,
		commandID,
		*msg.Destination,
		appconfig.DefaultLocationOfCurrent)

	// Skip sending response when the document requires a reboot
	if documentInfo.DocumentStatus == contracts.ResultStatusSuccessAndReboot {
		log.Debug("skipping sending response of %v since the document requires a reboot", *msg.MessageId)
		return
	}

	log.Debug("Sending reply on message completion ", outputs)
	sendResponse(*msg.MessageId, "", outputs)

	//persist : commands execution in completed folder (terminal state folder)
	log.Debugf("execution of %v is over. Moving interimState file from Current to Completed folder", *msg.MessageId)

	commandStateHelper.MoveCommandState(log,
		commandID,
		*msg.Destination,
		appconfig.DefaultLocationOfCurrent,
		appconfig.DefaultLocationOfCompleted)

	log.Debugf("Deleting message")
	isUpdate := false
	for pluginName := range pluginConfigurations {
		if pluginName == appconfig.PluginNameAwsAgentUpdate {
			isUpdate = true
		}
	}
	if !isUpdate {
		err = mdsService.DeleteMessage(log, *msg.MessageId)
		if err != nil {
			sdkutil.HandleAwsError(log, err, p.processorStopPolicy)
		}
	} else {
		log.Debug("MessageDeletion skipped as it will be handled by external process")
	}
}