예제 #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
// validateManifest makes sure all the fields are provided.
func validateManifest(log log.T, parsedManifest *Manifest, context *updateutil.InstanceContext, packageName string) error {
	if len(parsedManifest.URIFormat) == 0 {
		return fmt.Errorf("folder format cannot be null in the Manifest file")
	}
	fileName := context.FileName(packageName)
	foundPackage := false
	foundFile := false
	for _, p := range parsedManifest.Packages {
		if p.Name == packageName {
			log.Infof("found package %v", packageName)
			foundPackage = true
			for _, f := range p.Files {
				if f.Name == fileName {
					foundFile = true
					if len(f.AvailableVersions) == 0 {
						return fmt.Errorf("at least one available version is required for the %v", fileName)
					}

					log.Infof("found file %v", fileName)
					break
				}
			}
		}
	}

	if !foundPackage {
		return fmt.Errorf("cannot find the %v information in the Manifest file", packageName)
	}
	if !foundFile {
		return fmt.Errorf("cannot find the %v information in the Manifest file", fileName)
	}

	return nil
}
예제 #3
0
// install executes the install script for the specific version of agent
func installAgent(mgr *updateManager, log log.T, version string, context *UpdateContext) (err error) {
	log.Infof("Initiating %v %v installation", context.Current.PackageName, version)

	// find the path for the install script
	installerPath := updateutil.InstallerFilePath(
		context.Current.UpdateRoot,
		context.Current.PackageName,
		version)
	// calculate work directory
	workDir := updateutil.UpdateArtifactFolder(
		context.Current.UpdateRoot,
		context.Current.PackageName,
		version)

	// Install version
	if err = mgr.util.ExeCommand(
		log,
		installerPath,
		workDir,
		context.Current.UpdateRoot,
		context.Current.StdoutFileName,
		context.Current.StderrFileName,
		false); err != nil {

		return err
	}

	log.Infof("%v %v installed successfully", context.Current.PackageName, version)
	return nil
}
예제 #4
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
}
예제 #5
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
}
예제 #6
0
// verifyInstallation checks installation result, verifies if agent is running
func verifyInstallation(mgr *updateManager, log log.T, context *UpdateContext, isRollback bool) (err error) {
	// Check if agent is running
	var isRunning = false
	var instanceContext *updateutil.InstanceContext

	if instanceContext, err = mgr.util.CreateInstanceContext(log); err != nil {
		return mgr.failed(context, log, updateutil.ErrorEnvironmentIssue, err.Error(), false)
	}

	log.Infof("Initiating update health check")
	isRunning, err = mgr.util.IsServiceRunning(log, instanceContext)
	if err != nil || !isRunning {
		if !isRollback {
			message := updateutil.BuildMessage(err,
				"failed to update %v to %v, %v",
				context.Current.PackageName,
				context.Current.TargetVersion,
				"failed to start the agent")

			context.Current.AppendError(log, message)
			context.Current.AppendInfo(
				log,
				"Initiating rollback %v to %v",
				context.Current.PackageName,
				context.Current.SourceVersion)
			// Update state to rollback
			if err = mgr.inProgress(context, log, Rollback); err != nil {
				return err
			}
			return mgr.rollback(mgr, log, context)
		}

		message := updateutil.BuildMessage(err,
			"failed to rollback %v to %v, %v",
			context.Current.PackageName,
			context.Current.SourceVersion,
			"failed to start the agent")
		// Rolled back, but service cannot start, Update failed.
		return mgr.failed(context, log, updateutil.ErrorCannotStartService, message, false)
	}

	log.Infof("%v is running", context.Current.PackageName)
	if !isRollback {
		return mgr.succeeded(context, log)
	}

	message := fmt.Sprintf("rolledback %v to %v", context.Current.PackageName, context.Current.SourceVersion)
	log.Infof("message is %v", message)
	return mgr.failed(context, log, updateutil.ErrorCannotStartService, message, false)
}
예제 #7
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
}
예제 #8
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
}
예제 #9
0
// downloadAndUnzipArtifact downloads installation package and unzips it
func downloadAndUnzipArtifact(
	mgr *updateManager,
	log log.T,
	downloadInput artifact.DownloadInput,
	context *UpdateContext,
	version string) (err error) {

	log.Infof("Preparing source for version %v", version)
	// download installation zip files
	downloadOutput, err := downloadArtifact(log, downloadInput)
	if err != nil ||
		downloadOutput.IsHashMatched == false ||
		downloadOutput.LocalFilePath == "" {
		if err != nil {
			return fmt.Errorf("failed to download file reliably, %v", err.Error())
		}
		return fmt.Errorf("failed to download file reliably")
	}

	// downloaded successfully, append message
	context.Current.AppendInfo(log, "Successfully downloaded %v", downloadInput.SourceURL)

	// uncompress installation package
	if err = uncompress(
		downloadOutput.LocalFilePath,
		updateutil.UpdateArtifactFolder(context.Current.UpdateRoot, context.Current.PackageName, version)); err != nil {
		return fmt.Errorf("failed to uncompress installation package, %v", err.Error())
	}

	return nil
}
예제 #10
0
// prepareInstallationPackages downloads artifacts from public s3 storage
func prepareInstallationPackages(mgr *updateManager, log log.T, context *UpdateContext) (err error) {
	log.Infof("Initiating download %v", context.Current.PackageName)
	var instanceContext *updateutil.InstanceContext
	updateDownload := ""

	if instanceContext, err = mgr.util.CreateInstanceContext(log); err != nil {
		return mgr.failed(context, log, updateutil.ErrorEnvironmentIssue, err.Error(), false)
	}
	if err = validateUpdateVersion(log, context.Current, instanceContext); err != nil {
		return mgr.failed(context, log, updateutil.ErrorEnvironmentIssue, err.Error(), true)
	}

	if updateDownload, err = mgr.util.CreateUpdateDownloadFolder(); err != nil {
		message := updateutil.BuildMessage(
			err,
			"failed to create download folder %v %v",
			context.Current.PackageName,
			context.Current.TargetVersion)
		return mgr.failed(context, log, updateutil.ErrorEnvironmentIssue, message, true)
	}

	// Download source
	downloadInput := artifact.DownloadInput{
		SourceURL:            context.Current.SourceLocation,
		SourceHashValue:      context.Current.SourceHash,
		SourceHashType:       updateutil.HashType,
		DestinationDirectory: updateDownload,
	}

	if err = mgr.download(mgr, log, downloadInput, context, context.Current.SourceVersion); err != nil {
		return mgr.failed(context, log, updateutil.ErrorInvalidPackage, err.Error(), true)
	}

	// Download target
	downloadInput = artifact.DownloadInput{
		SourceURL:            context.Current.TargetLocation,
		SourceHashValue:      context.Current.TargetHash,
		SourceHashType:       updateutil.HashType,
		DestinationDirectory: updateDownload,
	}

	if err = mgr.download(mgr, log, downloadInput, context, context.Current.TargetVersion); err != nil {
		return mgr.failed(context, log, updateutil.ErrorInvalidPackage, err.Error(), true)
	}

	// Update stdout
	context.Current.AppendInfo(
		log,
		"Initiating %v update to %v",
		context.Current.PackageName,
		context.Current.TargetVersion)

	// Update state to Staged
	if err = mgr.inProgress(context, log, Staged); err != nil {
		return err
	}

	// Process update
	return mgr.update(mgr, log, context)
}
예제 #11
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)
	}
}
예제 #12
0
// AppendInfo appends messages to UpdateContext StandardOut
func (update *UpdateDetail) AppendInfo(log log.T, format string, params ...interface{}) {
	message := fmt.Sprintf(format, params...)
	log.Infof(message)
	if update.StandardOut != "" {
		update.StandardOut = fmt.Sprintf("%v\n%v", update.StandardOut, message)
	} else {
		update.StandardOut = message
	}
}
예제 #13
0
func isUpdateSupported(log log.T) (bool, error) {
	var sku string
	var err error

	// Get platform sku information
	if sku, err = getPlatformSku(log); err != nil {
		log.Infof("Failed to fetch sku - %v", err)
		return false, err
	}

	log.Infof("sku: %v", sku)

	// If sku represents nano server, return false
	if sku == ProductDataCenterNanoServer || sku == ProductStandardNanoServer {
		return false, nil
	}

	return true, nil
}
예제 #14
0
// IsDiskSpaceSufficientForUpdate loads disk space info and checks the available bytes
// Returns true if the system has at least 100 Mb for available disk space or false if it is less than 100 Mb
func (util *Utility) IsDiskSpaceSufficientForUpdate(log log.T) (bool, error) {
	var diskSpaceInfo fileutil.DiskSpaceInfo
	var err error

	// Get the available disk space
	if diskSpaceInfo, err = getDiskSpaceInfo(); err != nil {
		log.Infof("Failed to load disk space info - %v", err)
		return false, err
	}

	// Return false if available disk space is less than 100 Mb
	if diskSpaceInfo.AvailBytes < MinimumDiskSpaceForUpdate {
		log.Infof("Insufficient available disk space - %d Mb", diskSpaceInfo.AvailBytes/int64(1024*1024))
		return false, nil
	}

	// Return true otherwise
	return true, nil
}
예제 #15
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
}
예제 #16
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
}
예제 #17
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
}
예제 #18
0
// proceedUpdate starts update process
func proceedUpdate(mgr *updateManager, log log.T, context *UpdateContext) (err error) {
	log.Infof(
		"Attemping to upgrade from %v to %v",
		context.Current.SourceVersion,
		context.Current.TargetVersion)

	// Uninstall only when the target version is lower than the source version
	if context.Current.RequiresUninstall {
		if err = mgr.uninstall(mgr, log, context.Current.SourceVersion, context); err != nil {
			message := updateutil.BuildMessage(
				err,
				"failed to uninstall %v %v",
				context.Current.PackageName,
				context.Current.SourceVersion)
			return mgr.failed(context, log, updateutil.ErrorUninstallFailed, message, true)
		}
	}

	if err = mgr.install(mgr, log, context.Current.TargetVersion, context); err != nil {
		// Install target failed with err
		// log the error and initiating rollback to the source version
		message := updateutil.BuildMessage(err,
			"failed to install %v %v",
			context.Current.PackageName,
			context.Current.TargetVersion)
		context.Current.AppendError(log, message)

		context.Current.AppendInfo(
			log,
			"Initiating rollback %v to %v",
			context.Current.PackageName,
			context.Current.SourceVersion)
		// Update state to Rollback to indicate updater has initiated the rollback process
		if err = mgr.inProgress(context, log, Rollback); err != nil {
			return err
		}
		// Rollback
		return mgr.rollback(mgr, log, context)
	}

	// Update state to installed to indicate there is no error occur during installation
	// Updater has installed the new version and started the verify process
	if err = mgr.inProgress(context, log, Installed); err != nil {
		return err
	}

	// verify target version
	return mgr.verify(mgr, log, context, false)
}
예제 #19
0
// IsUpdateInProgress represents if the another update is running
func (context *UpdateContext) IsUpdateInProgress(log log.T) bool {
	//System will check the start time of the last update
	//If current system time minus start time is bigger than the MaxAllowedUpdateTime, which means update has been interrupted.
	//Allow system to resume update
	if context.Current == nil {
		return false
	} else if string(context.Current.State) == "" {
		return false
	} else {
		duration := time.Since(context.Current.StartDateTime)
		log.Infof("Attemping to retry update after %v seconds", duration.Seconds())
		if duration.Seconds() > maxAllowedUpdateDuration {
			return false
		}
	}

	return true
}
예제 #20
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
}
예제 #21
0
// Execute is a mocked method that just returns what mock tells it to.
func (m *MockCommandExecuter) Execute(log log.T, workingDir string, stdoutFilePath string, stderrFilePath string, cancelFlag task.CancelFlag, executionTimeout int, commandName string, commandArguments []string) (stdout io.Reader, stderr io.Reader, exitCode int, errs []error) {
	args := m.Called(log, workingDir, stdoutFilePath, stderrFilePath, cancelFlag, executionTimeout, commandName, commandArguments)
	log.Infof("args are %v", args)
	return args.Get(0).(io.Reader), args.Get(1).(io.Reader), args.Get(2).(int), args.Get(3).([]error)
}
예제 #22
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
}
예제 #23
0
// UploadOutputToS3Bucket is a mocked method that just returns what mock tells it to.
func (m *MockDefaultPlugin) UploadOutputToS3Bucket(log log.T, pluginID string, orchestrationDir string, outputS3BucketName string, outputS3KeyPrefix string, useTempDirectory bool, tempDir string, Stdout string, Stderr string) []string {
	args := m.Called(log, pluginID, orchestrationDir, outputS3BucketName, outputS3KeyPrefix, useTempDirectory, tempDir, Stdout, Stderr)
	log.Infof("args are %v", args)
	return args.Get(0).([]string)
}
예제 #24
0
// ExeCommand executes shell command
func (util *Utility) ExeCommand(
	log log.T,
	cmd string,
	workingDir string,
	updaterRoot string,
	stdOut string,
	stdErr string,
	isAsync bool) (err error) {

	parts := strings.Fields(cmd)

	if isAsync {
		command := execCommand(parts[0], parts[1:]...)
		command.Dir = workingDir
		prepareProcess(command)
		// Start command asynchronously
		err = cmdStart(command)
		if err != nil {
			return
		}
	} else {
		tempCmd := setPlatformSpecificCommand(parts)
		command := execCommand(tempCmd[0], tempCmd[1:]...)
		command.Dir = workingDir
		stdoutWriter, stderrWriter, exeErr := setExeOutErr(updaterRoot, stdOut, stdErr)
		if exeErr != nil {
			return exeErr
		}
		defer stdoutWriter.Close()
		defer stderrWriter.Close()

		command.Stdout = stdoutWriter
		command.Stderr = stderrWriter
		err = cmdStart(command)
		if err != nil {
			return
		}

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

		err = command.Wait()
		timedOut := !timer.Stop()
		if err != nil {
			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()
					if exitCode == -1 && timedOut {
						// set appropriate exit code based on cancel or timeout
						exitCode = pluginutil.CommandStoppedPreemptivelyExitCode
						log.Infof("The execution of command was timedout.")
					}
					err = fmt.Errorf("The execution of command returned Exit Status: %d \n %v", exitCode, err.Error())
				}
			}
			return err
		}
	}

	return nil
}
예제 #25
0
// updateAgent downloads the installation packages and update the agent
func runUpdateAgent(
	p *Plugin,
	config contracts.Configuration,
	log log.T,
	manager pluginHelper,
	util updateutil.T,
	rawPluginInput interface{},
	cancelFlag task.CancelFlag,
	outputS3BucketName string,
	outputS3KeyPrefix string,
	startTime time.Time) (out UpdatePluginOutput) {
	var pluginInput UpdatePluginInput
	var err error
	var context *updateutil.InstanceContext

	if isUpdateSupported, err := util.IsPlatformSupportedForUpdate(log); err == nil && !isUpdateSupported {
		out.Failed(log, fmt.Errorf("Unsupported platform for update"))
		return
	}

	if err = jsonutil.Remarshal(rawPluginInput, &pluginInput); err != nil {
		out.Failed(log,
			fmt.Errorf("invalid format in plugin properties %v;\nerror %v", rawPluginInput, err))
		return
	}

	if context, err = util.CreateInstanceContext(log); err != nil {
		out.Failed(log, err)
		return
	}

	//Use default manifest location is the override is not present
	if len(pluginInput.Source) == 0 {
		pluginInput.Source = p.ManifestLocation
	}
	//Calculate manifest location base on current instance's region
	pluginInput.Source = strings.Replace(pluginInput.Source, updateutil.RegionHolder, context.Region, -1)
	//Calculate updater package name base on agent name
	pluginInput.UpdaterName = pluginInput.AgentName + updateutil.UpdaterPackageNamePrefix
	//Generate update output
	targetVersion := pluginInput.TargetVersion
	if len(targetVersion) == 0 {
		targetVersion = "latest"
	}
	out.AppendInfo(log, "Updating %v from %v to %v",
		pluginInput.AgentName,
		version.Version,
		targetVersion)

	//Download manifest file
	manifest, downloadErr := manager.downloadManifest(log, util, &pluginInput, context, &out)
	if downloadErr != nil {
		out.Failed(log, downloadErr)
		return
	}

	//Validate update details
	noNeedToUpdate := false
	if noNeedToUpdate, err = manager.validateUpdate(log, &pluginInput, context, manifest, &out); noNeedToUpdate {
		if err != nil {
			out.Failed(log, err)
		}
		return
	}

	//Download updater and retrieve the version number
	updaterVersion := ""
	if updaterVersion, err = manager.downloadUpdater(
		log, util, pluginInput.UpdaterName, manifest, &out, context); err != nil {
		out.Failed(log, err)
		return
	}

	//Generate update command base on the update detail
	cmd := ""
	if cmd, err = manager.generateUpdateCmd(log,
		manifest,
		&pluginInput,
		context,
		updateutil.UpdaterFilePath(appconfig.UpdaterArtifactsRoot, pluginInput.UpdaterName, updaterVersion),
		config.MessageId,
		p.StdoutFileName,
		p.StderrFileName,
		outputS3KeyPrefix,
		outputS3BucketName); err != nil {
		out.Failed(log, err)
		return
	}
	log.Debugf("Update command %v", cmd)

	//Save update plugin result to local file, updater will read it during agent update
	updatePluginResult := &updateutil.UpdatePluginResult{
		StandOut:      out.Stdout,
		StartDateTime: startTime,
	}
	if err = util.SaveUpdatePluginResult(log, appconfig.UpdaterArtifactsRoot, updatePluginResult); err != nil {
		out.Failed(log, err)
		return
	}

	// If disk space is not sufficient, fail the update to prevent installation and notify user in output
	// If loading disk space fails, continue to update (agent update is backed by rollback handler)
	log.Infof("Checking available disk space ...")
	if isDiskSpaceSufficient, err := util.IsDiskSpaceSufficientForUpdate(log); err == nil && !isDiskSpaceSufficient {
		out.Failed(log, errors.New("Insufficient available disk space"))
		return
	}

	log.Infof("Start Installation")
	log.Infof("Hand over update process to %v", pluginInput.UpdaterName)
	//Execute updater, hand over the update process
	workDir := updateutil.UpdateArtifactFolder(
		appconfig.UpdaterArtifactsRoot, pluginInput.UpdaterName, updaterVersion)

	if err = util.ExeCommand(
		log,
		cmd,
		workDir,
		appconfig.UpdaterArtifactsRoot,
		p.StdoutFileName,
		p.StderrFileName,
		true); err != nil {
		out.Failed(log, err)
		return
	}

	out.Pending()
	return
}
예제 #26
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
}