예제 #1
0
// processRegistration handles flags related to the registration category
func processRegistration(log logger.T) (exitCode int) {
	if activationCode == "" || activationID == "" || region == "" {
		// clear registration
		if clear {
			return clearRegistration(log)
		}
		flagUsage()
		return 1
	}

	// check if previously registered
	if !force && registration.InstanceID() != "" {
		confirmation, err := askForConfirmation()
		if err != nil {
			log.Errorf("Registration failed due to %v", err)
			return 1
		}

		if !confirmation {
			log.Info("Registration canceled by user")
			return 1
		}
	}

	managedInstanceID, err := registerManagedInstance()
	if err != nil {
		log.Errorf("Registration failed due to %v", err)
		return 1
	}

	log.Infof("Successfully registered the instance with AWS SSM using Managed instance-id: %s", managedInstanceID)
	return 0
}
예제 #2
0
// initializeBookkeepingLocations - initializes all folder locations required for bookkeeping
func initializeBookkeepingLocations(log logger.T, instanceID string) bool {

	//Create folders pending, current, completed, corrupt under the location DefaultLogDirPath/<instanceId>
	log.Info("Initializing bookkeeping folders")
	initStatus := true
	folders := []string{
		appconfig.DefaultLocationOfPending,
		appconfig.DefaultLocationOfCurrent,
		appconfig.DefaultLocationOfCompleted,
		appconfig.DefaultLocationOfCorrupt}

	for _, folder := range folders {

		directoryName := path.Join(appconfig.DefaultDataStorePath,
			instanceID,
			appconfig.DefaultCommandRootDirName,
			appconfig.DefaultLocationOfState,
			folder)

		err := fileutil.MakeDirs(directoryName)
		if err != nil {
			log.Errorf("Encountered error while creating folders for internal state management. %v", err)
			initStatus = false
			break
		}
	}

	return initStatus
}
예제 #3
0
// processFingerprint handles flags related to the fingerprint category
func processFingerprint(log logger.T) (exitCode int) {
	if err := fingerprint.SetSimilarityThreshold(similarityThreshold); err != nil {
		log.Errorf("Error setting the SimilarityThreshold. %v", err)
		return 1
	}
	log.Infof("Fingerprint SimilarityTHreshold set to %v", similarityThreshold)
	return 0
}
예제 #4
0
// clearRegistration clears any existing registration data
func clearRegistration(log logger.T) (exitCode int) {
	err := registration.UpdateServerInfo("", "", "", "")
	if err == nil {
		log.Info("Registration information has been removed from the instance.")
		return 0
	}
	log.Errorf("error clearing the instance registration information. %v\nTry running as sudo/administrator.", err)
	return 1
}
예제 #5
0
// Run as a single process. Used by Unix systems and when running agent from console.
func run(log logger.T) {
	// run core manager
	cpm, err := start(log, instanceIDPtr, regionPtr)
	if err != nil {
		log.Errorf("error occured when starting amazon-ssm-agent: %v", err)
		return
	}
	blockUntilSignaled(log)
	stop(log, cpm)
}
예제 #6
0
func start(log logger.T, instanceIDPtr *string, regionPtr *string) (cpm *coremanager.CoreManager, err error) {
	log.Infof("Starting Agent: %v", version.String())
	log.Infof("OS: %s, Arch: %s", runtime.GOOS, runtime.GOARCH)
	log.Flush()

	if cpm, err = coremanager.NewCoreManager(instanceIDPtr, regionPtr, log); err != nil {
		log.Errorf("error occured when starting core manager: %v", err)
		return
	}
	cpm.Start()
	return
}
예제 #7
0
// uploadOutput uploads the stdout and stderr file to S3
func (c *contextManager) uploadOutput(log log.T, context *UpdateContext) (err error) {

	awsConfig := sdkutil.AwsConfig()
	var config appconfig.SsmagentConfig
	config, err = appconfig.Config(false)

	if err != nil {
		return fmt.Errorf("could not load config file: %v", err)
	}
	// If customers have provided override in app config, honor that.
	if config.S3.Region != "" {
		awsConfig.Region = &config.S3.Region
	}
	log.Infof("Uploading output files to region: %v", *awsConfig.Region)

	s3 := s3.New(session.New(awsConfig))

	// upload outputs (if any) to s3
	uploader := s3util.NewManager(s3)
	uploadOutputsToS3 := func() {
		// delete temp outputDir once we're done
		defer pluginutil.DeleteDirectory(log, updateutil.UpdateOutputDirectory(context.Current.UpdateRoot))

		// get stdout file path
		stdoutPath := updateutil.UpdateStandOutPath(context.Current.UpdateRoot, context.Current.StdoutFileName)
		s3Key := path.Join(context.Current.OutputS3KeyPrefix, context.Current.StdoutFileName)
		log.Debugf("Uploading %v to s3://%v/%v", stdoutPath, context.Current.OutputS3BucketName, s3Key)
		err = uploader.S3Upload(context.Current.OutputS3BucketName, s3Key, stdoutPath)
		if err != nil {
			log.Errorf("failed uploading %v to s3://%v/%v \n err:%v",
				stdoutPath,
				context.Current.OutputS3BucketName,
				s3Key,
				err)
		}

		// get stderr file path
		stderrPath := updateutil.UpdateStandOutPath(context.Current.UpdateRoot, context.Current.StderrFileName)
		s3Key = path.Join(context.Current.OutputS3KeyPrefix, context.Current.StderrFileName)
		log.Debugf("Uploading %v to s3://%v/%v", stderrPath, context.Current.OutputS3BucketName, s3Key)
		err = uploader.S3Upload(context.Current.OutputS3BucketName, s3Key, stderrPath)
		if err != nil {
			log.Errorf("failed uploading %v to s3://%v/%v \n err:%v", stderrPath, context.Current.StderrFileName, s3Key, err)
		}
	}

	uploadOutputsToS3()

	return nil
}
예제 #8
0
// HandleAwsError logs an AWS error.
func HandleAwsError(log log.T, err error, stopPolicy *StopPolicy) {
	if err != nil {
		// notice that we're using 1, so it will actually log the where
		// the error happened, 0 = this function, we don't want that.
		pc, fn, line, _ := runtime.Caller(1)
		log.Debugf("error in %s[%s:%d] %v", runtime.FuncForPC(pc).Name(), fn, line, err)

		// In case this is aws error, update the stop policy as well.
		if aErr, ok := err.(awserr.Error); ok {
			// Generic AWS Error with Code, Message, and original error (if any)
			log.Debugf("AWS error. Code: %v, Message: %v, origerror: %v ", aErr.Code(), aErr.Message(), aErr.OrigErr())

			// special treatment for Timeout exception - as this is expected
			if aErr.Code() == "RequestError" && aErr.OrigErr() != nil && strings.Contains(aErr.OrigErr().Error(), "Client.Timeout") {
				// resetting the error count to 0 - as these exceptions are all expected
				if stopPolicy != nil {
					resetStopPolicy(stopPolicy)
				}
				return
			}
		}

		log.Errorf("error when calling AWS APIs. error details - %v", err)
		if stopPolicy != nil {
			log.Infof("increasing error count by 1")
			stopPolicy.AddErrorCount(1)
		}

	} else {
		// there is no error,
		resetStopPolicy(stopPolicy)
	}
}
예제 #9
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
}
예제 #10
0
// runJob executes a job and then sends a signal on the given channel
func runJob(log log.T, job func(), doneChannel chan struct{}) {
	defer func() {
		// recover in case the job panics
		if msg := recover(); msg != nil {
			log.Errorf("Job failed with message %v", msg)
		}
		doneChannel <- struct{}{}
	}()
	job()
}
예제 #11
0
// RemoveData deletes the fileName from locationFolder under defaultLogDir/instanceID
func RemoveData(log log.T, commandID, instanceID, locationFolder string) {

	absoluteFileName := getCmdStateFileName(commandID, instanceID, locationFolder)

	err := fileutil.DeleteFile(absoluteFileName)
	if err != nil {
		log.Errorf("encountered error %v while deleting file %v", err, absoluteFileName)
	} else {
		log.Debugf("successfully deleted file %v", absoluteFileName)
	}
}
예제 #12
0
// ValidParameters checks if parameter names are valid. Returns valid parameters only.
func ValidParameters(log log.T, params map[string]interface{}) map[string]interface{} {
	validParams := make(map[string]interface{})
	for paramName, paramValue := range params {
		if validName(paramName) {
			validParams[paramName] = paramValue
		} else {
			log.Errorf("invalid parameter name %v", paramName)
		}
	}
	return validParams
}
예제 #13
0
// CreateScriptFile creates a script containing the given commands.
func CreateScriptFile(log log.T, scriptPath string, runCommand []string) (err error) {
	var sourceFile *os.File
	var scriptFile *os.File

	// create script file
	mode := int(appconfig.ReadWriteExecuteAccess)
	scriptFile, err = os.OpenFile(scriptPath, os.O_CREATE|os.O_WRONLY, os.FileMode(mode))
	if err != nil {
		log.Errorf("failed to create local script file %v, err %v", scriptPath, err)
		return
	}
	defer func() {
		cerr := scriptFile.Close()
		if err == nil {
			err = cerr
		}
	}()

	// write source commands to file
	if sourceFile != nil {
		if _, err = io.Copy(scriptFile, sourceFile); err != nil {
			log.Errorf("failed to write source scripts to file %v", scriptPath)
			return
		}
		_, err = scriptFile.WriteString("\n")
		if err != nil {
			log.Errorf("failed to write source scripts scripts to file %v", scriptPath)
			return
		}
	}

	// write source commands to file
	_, err = scriptFile.WriteString(strings.Join(runCommand, "\n") + "\n")
	if err != nil {
		log.Errorf("failed to write runcommand scripts to file %v", scriptPath)
		return
	}

	return
}
예제 #14
0
// finalizeUpdateAndSendReply completes the update and send reply to message service
func (u *updateManager) finalizeUpdateAndSendReply(log log.T, context *UpdateContext, errorCode string) (err error) {
	update := context.Current
	update.EndDateTime = time.Now().UTC()

	// resolve context location base on the UpdateRoot
	contextLocation := updateutil.UpdateContextFilePath(update.UpdateRoot)
	if err = u.ctxMgr.saveUpdateContext(log, context, contextLocation); err != nil {
		return err
	}

	// send reply
	if update.HasMessageID() {
		if err = u.svc.SendReply(log, update); err != nil {
			log.Errorf(err.Error())
		}

		if err = u.svc.DeleteMessage(log, update); err != nil {
			log.Errorf(err.Error())
		}
	}

	// update health information
	if err = u.svc.UpdateHealthCheck(log, update, errorCode); err != nil {
		log.Errorf(err.Error())
	}

	// upload output to s3 bucket
	log.Debugf("output s3 bucket name is %v", update.OutputS3BucketName)
	if update.OutputS3BucketName != "" {
		u.ctxMgr.uploadOutput(log, context)
	}

	context.cleanUpdate()
	if err = u.ctxMgr.saveUpdateContext(log, context, contextLocation); err != nil {
		return err
	}

	return nil
}
예제 #15
0
// FileCopy copies the content from reader to destinationPath file
func FileCopy(log log.T, destinationPath string, src io.Reader) (written int64, err error) {

	var file *os.File
	file, err = os.Create(destinationPath)
	if err != nil {
		log.Errorf("failed to create file. %v", err)
		return
	}
	defer file.Close()
	var size int64
	size, err = io.Copy(file, src)
	log.Infof("%s with %v bytes downloaded", destinationPath, size)
	return
}
예제 #16
0
// AppendError appends messages to UpdateContext StandardError and StandardOut
func (update *UpdateDetail) AppendError(log log.T, format string, params ...interface{}) {
	message := fmt.Sprintf(format, params...)
	log.Errorf(message)
	if update.StandardOut != "" {
		update.StandardOut = fmt.Sprintf("%v\n%v", update.StandardOut, message)
	} else {
		update.StandardOut = message
	}
	if update.StandardError != "" {
		update.StandardError = fmt.Sprintf("%v\n%v", update.StandardError, message)
	} else {
		update.StandardError = message
	}
}
예제 #17
0
// reboot is performed by running the following command
// shutdown -r -t 60
// The above command will cause the machine to reboot after 60 seconds
func reboot(log log.T) (err error) {
	log.Infof("rebooting the machine in %v seconds..", timeOutInSecondsBeforeReboot)
	command := exec.Command("shutdown", "-r", "-t", timeOutInSecondsBeforeReboot)
	var stdout, stderr bytes.Buffer
	command.Stderr = &stderr
	command.Stdout = &stdout
	err = command.Start()
	log.Infof("shutdown output: %v\n", stdout.String())

	if stderr.Len() != 0 {
		log.Errorf("shutdown error: %v\n", stderr.String())
	}
	return
}
예제 #18
0
// LoadParametersAsList returns properties as a list and appropriate PluginResult if error is encountered
func LoadParametersAsList(log log.T, prop interface{}) ([]interface{}, contracts.PluginResult) {

	var properties []interface{}
	var res contracts.PluginResult

	if err := jsonutil.Remarshal(prop, &properties); err != nil {
		log.Errorf("unable to parse plugin configuration")
		res.Output = "Execution failed because agent is unable to parse plugin configuration"
		res.Code = 1
		res.Status = contracts.ResultStatusFailed
	}

	return properties, res
}
예제 #19
0
// inProgress sets update to inProgressing with given new UpdateState
func (u *updateManager) inProgress(context *UpdateContext, log log.T, state UpdateState) (err error) {
	update := context.Current
	update.State = state
	update.Result = contracts.ResultStatusInProgress

	// resolve context location base on the UpdateRoot
	contextLocation := updateutil.UpdateContextFilePath(update.UpdateRoot)
	if err = u.ctxMgr.saveUpdateContext(log, context, contextLocation); err != nil {
		return err
	}

	if update.HasMessageID() {
		err = u.svc.SendReply(log, update)
		if err != nil {
			log.Errorf(err.Error())
		}
	}

	if err = u.svc.UpdateHealthCheck(log, update, ""); err != nil {
		log.Errorf(err.Error())
	}

	return nil
}
예제 #20
0
// reboot is performed by running the following command
// /sbin/shutdown -r +1
// The above command will cause the machine to reboot after 1 minute
func reboot(log log.T) (err error) {
	log.Infof("Rebooting the machine in %v Minutes..", timeOutInMinutesBeforeReboot)
	command := exec.Command("/sbin/shutdown", "-r", timeOutInMinutesBeforeReboot)
	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	var stdout, stderr bytes.Buffer
	command.Stderr = &stderr
	command.Stdout = &stdout
	err = command.Start()
	log.Infof("shutdown output: %v\n", stdout.String())

	if stderr.Len() != 0 {
		log.Errorf("shutdown error: %v\n", stderr.String())
	}
	return
}
예제 #21
0
// setCmdState persists given commandState
func setCmdState(log log.T, commandState message.CommandState, absoluteFileName, locationFolder string) {

	content, err := jsonutil.Marshal(commandState)
	if err != nil {
		log.Errorf("encountered error with message %v while marshalling %v to string", err, commandState)
	} else {
		if fileutil.Exists(absoluteFileName) {
			log.Debugf("overwriting contents of %v", absoluteFileName)
		}
		log.Tracef("persisting interim state %v in file %v", jsonutil.Indent(content), absoluteFileName)
		if s, err := fileutil.WriteIntoFileWithPermissions(absoluteFileName, jsonutil.Indent(content), os.FileMode(int(appconfig.ReadWriteAccess))); s && err == nil {
			log.Debugf("successfully persisted interim state in %v", locationFolder)
		} else {
			log.Debugf("persisting interim state in %v failed with error %v", locationFolder, err)
		}
	}
}
예제 #22
0
// extractIntFromString extracts a valid int value from a string.
func extractIntFromString(log log.T, input string) int {
	var iNum int
	var fNum float64
	var err error

	iNum, err = strconv.Atoi(input)
	if err == nil {
		return iNum
	}

	fNum, err = strconv.ParseFloat(input, 64)
	if err == nil {
		iNum = int(fNum)
		log.Infof("Unexpected 'TimeoutSeconds' float value %v received. Applying 'TimeoutSeconds' as %v", fNum, iNum)
	} else {
		log.Errorf("Unexpected 'TimeoutSeconds' string value %v received. Setting 'TimeoutSeconds' to default value %v", input, defaultExecutionTimeoutInSeconds)
		iNum = defaultExecutionTimeoutInSeconds
	}
	return iNum
}
예제 #23
0
// ValidateExecutionTimeout validates the supplied input interface and converts it into a valid int value.
func ValidateExecutionTimeout(log log.T, input interface{}) int {
	var num int

	switch input.(type) {
	case string:
		num = extractIntFromString(log, input.(string))
	case int:
		num = input.(int)
	case float64:
		f := input.(float64)
		num = int(f)
		log.Infof("Unexpected 'TimeoutSeconds' float value %v received. Applying 'TimeoutSeconds' as %v", f, num)
	default:
		log.Errorf("Unexpected 'TimeoutSeconds' value %v received. Setting 'TimeoutSeconds' to default value %v", input, defaultExecutionTimeoutInSeconds)
	}

	if num < minExecutionTimeoutInSeconds || num > maxExecutionTimeoutInSeconds {
		log.Infof("'TimeoutSeconds' value should be between %v and %v. Setting 'TimeoutSeconds' to default value %v", minExecutionTimeoutInSeconds, maxExecutionTimeoutInSeconds, defaultExecutionTimeoutInSeconds)
		num = defaultExecutionTimeoutInSeconds
	}
	return num
}
예제 #24
0
// PersistData stores the given object in the file-system in pretty Json indented format
// This will override the contents of an already existing file
func PersistData(log log.T, commandID, instanceID, locationFolder string, object interface{}) {

	lockDocument(commandID)
	defer unlockDocument(commandID)

	absoluteFileName := getCmdStateFileName(commandID, instanceID, locationFolder)

	content, err := jsonutil.Marshal(object)
	if err != nil {
		log.Errorf("encountered error with message %v while marshalling %v to string", err, object)
	} else {
		if fileutil.Exists(absoluteFileName) {
			log.Debugf("overwriting contents of %v", absoluteFileName)
		}
		log.Tracef("persisting interim state %v in file %v", jsonutil.Indent(content), absoluteFileName)
		if s, err := fileutil.WriteIntoFileWithPermissions(absoluteFileName, jsonutil.Indent(content), os.FileMode(int(appconfig.ReadWriteAccess))); s && err == nil {
			log.Debugf("successfully persisted interim state in %v", locationFolder)
		} else {
			log.Debugf("persisting interim state in %v failed with error %v", locationFolder, err)
		}
	}
}
예제 #25
0
// s3Download attempts to download a file via the aws sdk.
func s3Download(log log.T, amazonS3URL s3util.AmazonS3URL, destFile string) (output DownloadOutput, err error) {
	log.Debugf("attempting to download as s3 download %v", destFile)
	eTagFile := destFile + ".etag"

	config := &aws.Config{}
	var appConfig appconfig.SsmagentConfig
	appConfig, err = appconfig.Config(false)
	if err != nil {
		log.Error("failed to read appconfig.")
	} else {
		creds, err1 := appConfig.ProfileCredentials()
		if err1 != nil {
			config.Credentials = creds
		}
	}
	config.S3ForcePathStyle = aws.Bool(amazonS3URL.IsPathStyle)
	config.Region = aws.String(amazonS3URL.Region)

	params := &s3.GetObjectInput{
		Bucket: aws.String(amazonS3URL.Bucket),
		Key:    aws.String(amazonS3URL.Key),
	}

	if fileutil.Exists(destFile) == true && fileutil.Exists(eTagFile) == true {
		var existingETag string
		existingETag, err = fileutil.ReadAllText(eTagFile)
		if err != nil {
			log.Debugf("failed to read etag file %v, %v", eTagFile, err)
			return
		}
		params.IfNoneMatch = aws.String(existingETag)
	}

	s3client := s3.New(session.New(config))

	req, resp := s3client.GetObjectRequest(params)
	err = req.Send()
	if err != nil {
		if req.HTTPResponse == nil || req.HTTPResponse.StatusCode != http.StatusNotModified {
			log.Debug("failed to download from s3, ", err)
			fileutil.DeleteFile(destFile)
			fileutil.DeleteFile(eTagFile)
			return
		}

		log.Debugf("Unchanged file.")
		output.IsUpdated = false
		output.LocalFilePath = destFile
		return output, nil
	}

	if *resp.ETag != "" {
		log.Debug("files etag is ", *resp.ETag)
		err = fileutil.WriteAllText(eTagFile, *resp.ETag)
		if err != nil {
			log.Errorf("failed to write eTagfile %v, %v ", eTagFile, err)
			return
		}
	}

	defer resp.Body.Close()
	_, err = FileCopy(log, destFile, resp.Body)
	if err == nil {
		output.LocalFilePath = destFile
		output.IsUpdated = true
	} else {
		log.Errorf("failed to write destFile %v, %v ", destFile, err)
	}
	return
}
예제 #26
0
// UploadOutputToS3Bucket uploads outputs (if any) to s3
func (p *DefaultPlugin) UploadOutputToS3Bucket(log log.T, pluginID string, orchestrationDir string, outputS3BucketName string, outputS3KeyPrefix string, useTempDirectory bool, tempDir string, Stdout string, Stderr string) []string {
	var uploadOutputToS3BucketErrors []string
	if outputS3BucketName != "" {
		uploadOutputsToS3 := func() {
			uploadToS3 := true
			var testUploadError error

			if region, err := platform.Region(); err == nil && region != s3Bjs {
				p.Uploader.SetS3ClientRegion(S3RegionUSStandard)
			}

			log.Infof("uploading a test file to s3 bucket - %v , s3 key - %v with S3Client using region endpoint - %v",
				outputS3BucketName,
				outputS3KeyPrefix,
				p.Uploader.GetS3ClientRegion())

			testUploadError = p.Uploader.UploadS3TestFile(log, outputS3BucketName, outputS3KeyPrefix)

			if testUploadError != nil {
				//Check if the error is related to Access Denied - i.e missing permissions
				if p.Uploader.IsS3ErrorRelatedToAccessDenied(testUploadError.Error()) {
					log.Debugf("encountered access denied related error - can't upload to S3 due to missing permissions -%v", testUploadError.Error())
					uploadToS3 = false
					//since we don't have permissions - no S3 calls will go through no matter what
				} else if p.Uploader.IsS3ErrorRelatedToWrongBucketRegion(testUploadError.Error()) { //check if error is related to different bucket region

					log.Debugf("encountered error related to wrong bucket region while uploading test file to S3 - %v. parsing the message to get expected region",
						testUploadError.Error())

					expectedBucketRegion := p.Uploader.GetS3BucketRegionFromErrorMsg(log, testUploadError.Error())

					//set the region to expectedBucketRegion
					p.Uploader.SetS3ClientRegion(expectedBucketRegion)
				} else {
					log.Debugf("encountered unexpected error while uploading test file to S3 - %v, no need to modify s3client", testUploadError.Error())
				}
			} else { //there were no errors while uploading a test file to S3 - our s3client should continue to use "us-east-1"

				log.Debugf("there were no errors while uploading a test file to S3 in region - %v. S3 client will continue to use region - %v",
					S3RegionUSStandard,
					p.Uploader.GetS3ClientRegion())
			}

			if uploadToS3 {
				log.Infof("uploading logs to S3 with client configured to use region - %v", p.Uploader.GetS3ClientRegion())

				if useTempDirectory {
					// delete temp directory once we're done
					defer DeleteDirectory(log, tempDir)
				}

				if Stdout != "" {
					localPath := filepath.Join(orchestrationDir, p.StdoutFileName)
					s3Key := path.Join(outputS3KeyPrefix, pluginID, p.StdoutFileName)
					log.Debugf("Uploading %v to s3://%v/%v", localPath, outputS3BucketName, s3Key)
					err := p.Uploader.S3Upload(outputS3BucketName, s3Key, localPath)
					if err != nil {

						log.Errorf("failed uploading %v to s3://%v/%v err:%v", localPath, outputS3BucketName, s3Key, err)
						if p.UploadToS3Sync {
							// if we are in synchronous mode, we can also return the error
							uploadOutputToS3BucketErrors = append(uploadOutputToS3BucketErrors, err.Error())
						}
					}
				}

				if Stderr != "" {
					localPath := filepath.Join(orchestrationDir, p.StderrFileName)
					s3Key := path.Join(outputS3KeyPrefix, pluginID, p.StderrFileName)
					log.Debugf("Uploading %v to s3://%v/%v", localPath, outputS3BucketName, s3Key)
					err := p.Uploader.S3Upload(outputS3BucketName, s3Key, localPath)
					if err != nil {
						log.Errorf("failed uploading %v to s3://%v/%v err:%v", localPath, outputS3BucketName, s3Key, err)
						if p.UploadToS3Sync {
							// if we are in synchronous mode, we can also return the error
							uploadOutputToS3BucketErrors = append(uploadOutputToS3BucketErrors, err.Error())
						}
					}
				}
			} else {
				//TODO:Bubble this up to engine - so that document level status reply can be sent stating no permissions to perform S3 upload - similar to ec2config
			}
		}

		if p.UploadToS3Sync {
			uploadOutputsToS3()
		} else {
			go uploadOutputsToS3()
		}
	}

	//return out.Errors
	return uploadOutputToS3BucketErrors
}
예제 #27
0
// runCommands executes one set of commands and returns their output.
func (p *Plugin) runCommands(log log.T, pluginInput PSModulePluginInput, orchestrationDirectory string, cancelFlag task.CancelFlag, outputS3BucketName string, outputS3KeyPrefix string) (out PSModulePluginOutput) {
	var err error

	// if no orchestration directory specified, create temp directory
	var useTempDirectory = (orchestrationDirectory == "")
	var tempDir string
	if useTempDirectory {
		if tempDir, err = ioutil.TempDir("", "Ec2RunCommand"); err != nil {
			out.Errors = append(out.Errors, err.Error())
			log.Error(err)
			return
		}
		orchestrationDirectory = tempDir
	}

	orchestrationDir := fileutil.RemoveInvalidChars(filepath.Join(orchestrationDirectory, pluginInput.ID))
	log.Debugf("Running commands %v in workingDirectory %v; orchestrationDir %v ", pluginInput.RunCommand, pluginInput.WorkingDirectory, orchestrationDir)

	// create orchestration dir if needed
	if err = fileutil.MakeDirsWithExecuteAccess(orchestrationDir); err != nil {
		log.Debug("failed to create orchestrationDir directory", orchestrationDir)
		out.Errors = append(out.Errors, err.Error())
		return
	}

	// Create script file path
	scriptPath := filepath.Join(orchestrationDir, pluginutil.RunCommandScriptName)
	log.Debugf("Writing commands %v to file %v", pluginInput, scriptPath)

	// Create script file
	if err = pluginutil.CreateScriptFile(log, scriptPath, pluginInput.RunCommand); err != nil {
		out.Errors = append(out.Errors, err.Error())
		log.Errorf("failed to create script file. %v", err)
		return
	}

	// Download file from source if available
	downloadOutput, err := pluginutil.DownloadFileFromSource(log, pluginInput.Source, pluginInput.SourceHash, pluginInput.SourceHashType)
	if err != nil || downloadOutput.IsHashMatched == false || downloadOutput.LocalFilePath == "" {
		errorString := fmt.Errorf("failed to download file reliably %v", pluginInput.Source)
		out.MarkAsFailed(log, errorString)
		return
	} else {
		// Uncompress the zip file received
		fileutil.Uncompress(downloadOutput.LocalFilePath, PowerShellModulesDirectory)
	}

	// Set execution time
	executionTimeout := pluginutil.ValidateExecutionTimeout(log, pluginInput.TimeoutSeconds)

	// Create output file paths
	stdoutFilePath := filepath.Join(orchestrationDir, p.StdoutFileName)
	stderrFilePath := filepath.Join(orchestrationDir, p.StderrFileName)
	log.Debugf("stdout file %v, stderr file %v", stdoutFilePath, stderrFilePath)

	// Construct Command Name and Arguments
	commandName := pluginutil.GetShellCommand()
	commandArguments := append(pluginutil.GetShellArguments(), scriptPath, pluginutil.ExitCodeTrap)

	// Execute Command
	stdout, stderr, exitCode, errs := p.ExecuteCommand(log, pluginInput.WorkingDirectory, stdoutFilePath, stderrFilePath, cancelFlag, executionTimeout, commandName, commandArguments)

	// Set output status
	out.ExitCode = exitCode
	out.Status = pluginutil.GetStatus(out.ExitCode, cancelFlag)

	if len(errs) > 0 {
		for _, err := range errs {
			out.Errors = append(out.Errors, err.Error())
			if out.Status != contracts.ResultStatusCancelled &&
				out.Status != contracts.ResultStatusTimedOut &&
				out.Status != contracts.ResultStatusSuccessAndReboot {
				log.Error("failed to run commands: ", err)
				out.Status = contracts.ResultStatusFailed
			}
		}
	}

	// read (a prefix of) the standard output/error
	out.Stdout, err = pluginutil.ReadPrefix(stdout, p.MaxStdoutLength, p.OutputTruncatedSuffix)
	if err != nil {
		out.Errors = append(out.Errors, err.Error())
		log.Error(err)
	}
	out.Stderr, err = pluginutil.ReadPrefix(stderr, p.MaxStderrLength, p.OutputTruncatedSuffix)
	if err != nil {
		out.Errors = append(out.Errors, err.Error())
		log.Error(err)
	}

	// Upload output to S3
	uploadOutputToS3BucketErrors := p.ExecuteUploadOutputToS3Bucket(log, pluginInput.ID, orchestrationDir, outputS3BucketName, outputS3KeyPrefix, useTempDirectory, tempDir, out.Stdout, out.Stderr)
	out.Errors = append(out.Errors, uploadOutputToS3BucketErrors...)

	// Return Json indented response
	responseContent, _ := jsonutil.Marshal(out)
	log.Debug("Returning response:\n", jsonutil.Indent(responseContent))
	return
}
예제 #28
0
// RunCommand runs the given commands using the given working directory.
// Standard output and standard error are sent to the given writers.
func RunCommand(log log.T,
	cancelFlag task.CancelFlag,
	workingDir string,
	stdoutWriter io.Writer,
	stderrWriter io.Writer,
	executionTimeout int,
	commandName string,
	commandArguments []string,
) (exitCode int, err error) {

	command := exec.Command(commandName, commandArguments...)
	command.Dir = workingDir
	command.Stdout = stdoutWriter
	command.Stderr = stderrWriter
	exitCode = 0

	// configure OS-specific process settings
	prepareProcess(command)

	log.Debug()
	log.Debugf("Running in directory %v, command: %v %v.", workingDir, commandName, commandArguments)
	log.Debug()
	if err = command.Start(); err != nil {
		log.Error("error occurred starting the command", err)
		exitCode = 1
		return
	}

	go killProcessOnCancel(log, command, cancelFlag)

	timer := time.NewTimer(time.Duration(executionTimeout) * time.Second)
	go killProcessOnTimeout(log, command, timer)

	err = command.Wait()
	timedOut := !timer.Stop() // returns false if called previously - indicates timedOut.
	if err != nil {
		exitCode = 1
		log.Debugf("command failed to run %v", err)
		if exiterr, ok := err.(*exec.ExitError); ok {
			// The program has exited with an exit code != 0
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				exitCode = status.ExitStatus()
				// First try to handle Cancel and Timeout scenarios
				// SIGKILL will result in an exitcode of -1
				if exitCode == -1 {
					if cancelFlag.Canceled() {
						// set appropriate exit code based on cancel or timeout
						exitCode = pluginutil.CommandStoppedPreemptivelyExitCode
						log.Infof("The execution of command was cancelled.")
					} else if timedOut {
						// set appropriate exit code based on cancel or timeout
						exitCode = pluginutil.CommandStoppedPreemptivelyExitCode
						log.Infof("The execution of command was timedout.")
					}
				} else {
					log.Infof("The execution of command returned Exit Status: %d", exitCode)
				}
			}
		}
	} else {
		// check if cancellation or timeout failed to kill the process
		// This will not occur as we do a SIGKILL, which is not recoverable.
		if cancelFlag.Canceled() {
			// This is when the cancellation failed and the command completed successfully
			log.Errorf("the cancellation failed to stop the process.")
			// do not return as the command could have been cancelled and also timedout
		}
		if timedOut {
			// This is when the timeout failed and the command completed successfully
			log.Errorf("the timeout failed to stop the process.")
		}
	}

	log.Debug("Done waiting!")
	return
}
예제 #29
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
}
예제 #30
0
// httpDownload attempts to download a file via http/s call
func httpDownload(log log.T, fileURL string, destFile string) (output DownloadOutput, err error) {
	log.Debugf("attempting to download as http/https download %v", destFile)
	eTagFile := destFile + ".etag"
	var check http.Client
	var request *http.Request
	request, err = http.NewRequest("GET", fileURL, nil)
	if err != nil {
		return
	}
	if fileutil.Exists(destFile) == true && fileutil.Exists(eTagFile) == true {
		var existingETag string
		existingETag, err = fileutil.ReadAllText(eTagFile)
		request.Header.Add("If-None-Match", existingETag)
	}

	check = http.Client{
		CheckRedirect: func(r *http.Request, via []*http.Request) error {
			r.URL.Opaque = r.URL.Path
			return nil
		},
	}

	var resp *http.Response
	resp, err = check.Do(request)
	if err != nil {
		log.Debug("failed to download from http/https, ", err)
		fileutil.DeleteFile(destFile)
		fileutil.DeleteFile(eTagFile)
		return
	}

	if resp.StatusCode == http.StatusNotModified {
		log.Debugf("Unchanged file.")
		output.IsUpdated = false
		output.LocalFilePath = destFile
		return output, nil
	} else if resp.StatusCode != http.StatusOK {
		log.Debug("failed to download from http/https, ", err)
		fileutil.DeleteFile(destFile)
		fileutil.DeleteFile(eTagFile)
		err = fmt.Errorf("http request failed. status:%v statuscode:%v", resp.Status, resp.StatusCode)
		return
	}
	defer resp.Body.Close()
	eTagValue := resp.Header.Get("Etag")
	if eTagValue != "" {
		log.Debug("file eTagValue is ", eTagValue)
		err = fileutil.WriteAllText(eTagFile, eTagValue)
		if err != nil {
			log.Errorf("failed to write eTagfile %v, %v ", eTagFile, err)
			return
		}
	}
	_, err = FileCopy(log, destFile, resp.Body)
	if err == nil {
		output.LocalFilePath = destFile
		output.IsUpdated = true
	} else {
		log.Errorf("failed to write destFile %v, %v ", destFile, err)
	}
	return
}