Exemple #1
0
// getCmdState reads commandState from given file
func getCmdState(log log.T, fileName string) message.CommandState {

	var commandState message.CommandState
	err := jsonutil.UnmarshalFile(fileName, &commandState)
	if err != nil {
		log.Errorf("encountered error with message %v while reading Interim state of command from file - %v", err, fileName)
	} else {
		//logging interim state as read from the file
		jsonString, err := jsonutil.Marshal(commandState)
		if err != nil {
			log.Errorf("encountered error with message %v while marshalling %v to string", err, commandState)
		} else {
			log.Tracef("interim CommandState read from file-system - %v", jsonutil.Indent(jsonString))
		}
	}

	return commandState
}
Exemple #2
0
// Config loads the app configuration for amazon-ssm-agent.
// If reload is true, it loads the config afresh,
// otherwise it returns a previous loaded version, if any.
func Config(reload bool) (SsmagentConfig, error) {
	if reload || !isLoaded() {
		var agentConfig SsmagentConfig
		agentConfig = DefaultConfig()
		path, pathErr := getAppConfigPath()
		if pathErr != nil {
			return agentConfig, nil
		}

		// Process config override
		fmt.Printf("Applying config override from %s.\n", path)

		if err := jsonutil.UnmarshalFile(path, &agentConfig); err != nil {
			fmt.Println("Failed to unmarshal config override. Fall back to default.")
			return agentConfig, err
		}
		agentConfig.Os.Name = runtime.GOOS
		agentConfig.Agent.Version = version.Version
		parser(&agentConfig)
		cache(agentConfig)
	}
	return getCached(), 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
}
// processOlderMessagesFromCurrent processes older messages that were persisted in CURRENT folder
func (p *Processor) processMessagesFromCurrent(instanceID string) {
	log := p.context.Log()
	config := p.context.AppConfig()

	unprocessedMsgsLocation := path.Join(appconfig.DefaultDataStorePath,
		instanceID,
		appconfig.DefaultCommandRootDirName,
		appconfig.DefaultLocationOfState,
		appconfig.DefaultLocationOfCurrent)

	if isDirectoryEmpty, _ := fileutil.IsDirEmpty(unprocessedMsgsLocation); isDirectoryEmpty {

		log.Debugf("no older messages to process from %v", unprocessedMsgsLocation)

	} else {

		//get all older messages from previous run of agent
		files, err := ioutil.ReadDir(unprocessedMsgsLocation)

		//TODO:  revisit this when bookkeeping is made invasive
		if err != nil {
			log.Errorf("skipping reading inprogress messages from %v. unexpected error encountered - %v", unprocessedMsgsLocation, err)
			return
		}

		//iterate through all old executing messages
		for _, f := range files {
			log.Debugf("processing previously unexecuted message - %v", f.Name())

			//construct the absolute path - safely assuming that interim state for older messages are already present in Current folder
			file := path.Join(appconfig.DefaultDataStorePath,
				instanceID,
				appconfig.DefaultCommandRootDirName,
				appconfig.DefaultLocationOfState,
				appconfig.DefaultLocationOfCurrent,
				f.Name())

			var oldCmdState messageContracts.CommandState

			//parse the message
			//TODO:  Not all messages in Current folder correspond to SendCommand.
			if err := jsonutil.UnmarshalFile(file, &oldCmdState); err != nil {
				log.Errorf("skipping processsing of previously unexecuted messages. encountered error %v while reading unprocessed message from file - %v", err, f)
			} else {
				if oldCmdState.DocumentInformation.RunCount >= config.Mds.CommandRetryLimit {
					//TODO:  Move command to corrupt/failed
					// do not process as the command has failed too many times
					break
				}

				pluginOutputs := make(map[string]*contracts.PluginResult)

				// increment the command run count
				oldCmdState.DocumentInformation.RunCount++
				// Update reboot status
				for v := range oldCmdState.PluginsInformation {
					plugin := oldCmdState.PluginsInformation[v]
					if plugin.HasExecuted && plugin.Result.Status == contracts.ResultStatusSuccessAndReboot {
						log.Debugf("plugin %v has completed a reboot. Setting status to Success.", v)
						plugin.Result.Status = contracts.ResultStatusSuccess
						oldCmdState.PluginsInformation[v] = plugin
						pluginOutputs[v] = &plugin.Result
						p.sendResponse(oldCmdState.DocumentInformation.MessageID, v, pluginOutputs)
					}
				}

				// func PersistData(log log.T, commandID, instanceID, locationFolder string, object interface{}) {
				commandStateHelper.PersistData(log, oldCmdState.DocumentInformation.CommandID, instanceID, appconfig.DefaultLocationOfCurrent, oldCmdState)

				//Submit the work to Job Pool so that we don't block for processing of new messages
				err := p.sendCommandPool.Submit(log, oldCmdState.DocumentInformation.MessageID, func(cancelFlag task.CancelFlag) {
					p.runCmdsUsingCmdState(p.context.With("[messageID="+oldCmdState.DocumentInformation.MessageID+"]"),
						p.service,
						p.pluginRunner,
						cancelFlag,
						p.buildReply,
						p.sendResponse,
						oldCmdState)
				})
				if err != nil {
					log.Error("SendCommand failed for previously unexecuted commands", err)
					return
				}
			}
		}
	}
}
Exemple #5
0
func main() {
	defer log.Flush()
	commandPtr := flag.String("c", "", "a command")
	scriptFilePtr := flag.String("f", "", "a script file")
	dirPtr := flag.String("d", "", "working directory")
	bucketNamePtr := flag.String("b", "", "bucket name")
	keyPrefixPtr := flag.String("k", "", "bucket key prefix")
	cancelPtr := flag.Bool("cancel", false, "cancel command on key press")
	typePtr := flag.String("type", "", "instance type (windows, ubuntu or aml)")
	instanceIDPtr := flag.String("i", "", "instance id")
	regionPtr := flag.String("r", "us-east-1", "instance region")
	flag.Parse()
	var timeout int64 = 10000
	timeoutPtr := &timeout
	var err error
	err = platform.SetRegion(*regionPtr)
	if err != nil {
		log.Error("please specify the region to use.")
		return
	}

	if *commandPtr == "" && *scriptFilePtr == "" {
		fmt.Println("No commands specified (use either -c or -f).")
		flag.Usage()
		return
	}
	if *keyPrefixPtr == "" {
		keyPrefixPtr = nil
	}
	if *bucketNamePtr == "" {
		bucketNamePtr = nil
		if keyPrefixPtr != nil {
			defaultBucket := "ec2configservice-ssm-logs"
			bucketNamePtr = &defaultBucket
		}
	}

	var cc consoleConfig
	err = jsonutil.UnmarshalFile("integration-cli.json", &cc)
	if err != nil {
		log.Error("error parsing consoleConfig ", err)
		return
	}

	// specific instance is provided use only that
	if *instanceIDPtr != "" {
		cc.Instances = make(map[string]string)
		if *typePtr != "" {
			cc.Instances[*instanceIDPtr] = *typePtr
		} else {
			cc.Instances[*instanceIDPtr] = "aml"
		}
	} else {
		// other wise select or filter from the consoleConfig file list
		if *typePtr != "" {
			for instanceID, instanceType := range cc.Instances {
				if instanceType != *typePtr {
					delete(cc.Instances, instanceID)
				}
			}
			if len(cc.Instances) == 0 {
				log.Error("no instances of type ", *typePtr)
				return
			}
		}
	}

	ssmSvc := ssm.NewService()
	if ssmSvc == nil {
		log.Error("couldn't create ssm service.")
		return
	}

	var instanceIDs []string
	// first get windows instances (bug in SSM if first instance is not win)
	for instanceID, instanceType := range cc.Instances {
		if instanceType == "windows" {
			instanceIDs = append(instanceIDs, instanceID)
		}
	}
	// then get rest of the instances
	for instanceID, instanceType := range cc.Instances {
		if instanceType != "windows" {
			instanceIDs = append(instanceIDs, instanceID)
		}
	}
	docName := "AWS-BETA-RunShellScript"
	if runtime.GOOS == "windows" {
		docName = "AWS-RunPowerShellScript"
	}

	var commands []string
	if *commandPtr != "" {
		commands = []string{*commandPtr}
	} else {
		f, err := os.Open(*scriptFilePtr)
		if err != nil {
			log.Error(err)
			return
		}
		scanner := bufio.NewScanner(f)
		for scanner.Scan() {
			commands = append(commands, scanner.Text())
		}
	}

	for i := 0; i < len(commands); i++ {
		// escape backslashes
		commands[i] = strings.Replace(commands[i], `\`, `\\`, -1)

		// escape double quotes
		commands[i] = strings.Replace(commands[i], `"`, `\"`, -1)

		// escape single quotes
		//		commands[i] = strings.Replace(commands[i], `'`, `\'`, -1)
	}

	parameters := map[string][]*string{
		"commands":         aws.StringSlice(commands),
		"workingDirectory": aws.StringSlice([]string{*dirPtr}),
	}

	log.Infof("Sending command %v", parameters)

	cmd, err := ssmSvc.SendCommand(log, docName, instanceIDs, parameters, timeoutPtr, bucketNamePtr, keyPrefixPtr)
	if cmd == nil || cmd.Command == nil || cmd.Command.CommandId == nil {
		log.Error("command was not created. Aborting!")
		return
	}

	if *cancelPtr {
		log.Info("Press any key to cancel command")
		var b = make([]byte, 1)
		os.Stdin.Read(b)
		log.Info("Canceling command ")
		ssmSvc.CancelCommand(log, *cmd.Command.CommandId, instanceIDs)
	}

	log.Info("================== Looping for results ================")
	log.Flush()
	time.Sleep(1000 * time.Millisecond)
	for {
		done := true
	inst:
		for instanceID, instanceType := range cc.Instances {
			descr := fmt.Sprintf("%v [%v]", instanceID, instanceType)
			out, err := ssmSvc.ListCommandInvocations(log, instanceID, *cmd.Command.CommandId)
			if err != nil {
				continue
			}
			for _, inv := range out.CommandInvocations {
				if *inv.Status == "Pending" {
					log.Infof("Instance %v is in status %v; waiting some more", descr, *inv.Status)
					done = false
					continue inst
				}

				data, err := json.Marshal(inv)
				if err != nil {
					log.Error(err)
					continue
				}
				log.Debug(jsonutil.Indent(string(data)))

				for _, cp := range inv.CommandPlugins {
					if cp.Output == nil {
						log.Errorf("Output Nil for %v", descr)
						continue
					}

					var o interface{}
					err := json.Unmarshal([]byte(*cp.Output), &o)
					if err != nil {
						log.Errorf("error parsing %v\n err=%v", *cp.Output, err)
					}
					log.Info(descr, " : ", prettyPrint(o, 0))
				}
			}
		}
		if done {
			break
		}
		time.Sleep(3000 * time.Millisecond)
	}

	//	c.Wait()*/
}