Example #1
0
//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(&params)
	if err != nil {
		sdkutil.HandleAwsError(log, err, ssmStopPolicy)
		return
	}
	log.Debug("UpdateInstanceInformation Response", response)
	return
}
Example #2
0
// 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
}
Example #4
0
// 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
}
Example #5
0
// 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,
	}
}