// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may not // use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, // either express or implied. See the License for the specific language governing // permissions and limitations under the License. // Package parser contains utilities for parsing and encoding MDS/SSM messages. package parser import ( "encoding/json" "fmt" "path" "path/filepath" "time" "github.com/aws/amazon-ssm-agent/agent/appconfig" "github.com/aws/amazon-ssm-agent/agent/association/model" "github.com/aws/amazon-ssm-agent/agent/context" "github.com/aws/amazon-ssm-agent/agent/contracts" "github.com/aws/amazon-ssm-agent/agent/framework/docparser" "github.com/aws/amazon-ssm-agent/agent/jsonutil" "github.com/aws/amazon-ssm-agent/agent/log" messageContracts "github.com/aws/amazon-ssm-agent/agent/runcommand/contracts" "github.com/aws/amazon-ssm-agent/agent/times" ) // ErrorMsg represents the error message to be sent to the customer const ErrorMsg = "Encountered error while parsing input - internal error" // ParseDocumentForPayload parses an document and replaces the parameters where needed. func ParseDocumentForPayload(log log.T, rawData *model.InstanceAssociation) (*messageContracts.SendCommandPayload, error) { rawDataContent, err := jsonutil.Marshal(rawData) if err != nil { log.Debugf("Could not marshal association! ", err) return nil, fmt.Errorf("%v", ErrorMsg) } log.Debug("Processing association ", jsonutil.Indent(rawDataContent)) payload := &messageContracts.SendCommandPayload{} if err = json.Unmarshal([]byte(*rawData.Document), &payload.DocumentContent); err != nil { log.Debugf("Could not unmarshal parameters ", err) return nil, fmt.Errorf("%v", ErrorMsg) } log.Debugf("unmarshal parameters: %v", payload.DocumentContent) payload.DocumentName = *rawData.Association.Name payload.CommandID = *rawData.Association.AssociationId if rawData.Association.OutputLocation != nil && rawData.Association.OutputLocation.S3Location != nil { if rawData.Association.OutputLocation.S3Location.OutputS3KeyPrefix != nil { payload.OutputS3KeyPrefix = *rawData.Association.OutputLocation.S3Location.OutputS3KeyPrefix } if rawData.Association.OutputLocation.S3Location.OutputS3BucketName != nil { payload.OutputS3BucketName = *rawData.Association.OutputLocation.S3Location.OutputS3BucketName } } payload.Parameters = docparser.ParseParameters(log, rawData.Association.Parameters, payload.DocumentContent.Parameters) return payload, nil } // InitializeDocumentState - an interim state that is used around during an execution of a document func InitializeDocumentState(context context.T, payload *messageContracts.SendCommandPayload, rawData *model.InstanceAssociation) (contracts.DocumentState, error) { //initialize document information with relevant values extracted from msg documentInfo := newDocumentInfo(rawData, payload) // adapt plugin configuration format from MDS to plugin expected format s3KeyPrefix := path.Join(payload.OutputS3KeyPrefix, documentInfo.InstanceID, documentInfo.AssociationID, documentInfo.RunID) orchestrationRootDir := filepath.Join( appconfig.DefaultDataStorePath, documentInfo.InstanceID, appconfig.DefaultDocumentRootDirName, context.AppConfig().Agent.OrchestrationRootDir) orchestrationDir := filepath.Join(orchestrationRootDir, documentInfo.AssociationID, documentInfo.RunID) parserInfo := docparser.DocumentParserInfo{ OrchestrationDir: orchestrationDir, S3Bucket: payload.OutputS3BucketName, S3Prefix: s3KeyPrefix, MessageId: documentInfo.MessageID, DocumentId: documentInfo.DocumentID, } docContent := &docparser.DocContent{ SchemaVersion: payload.DocumentContent.SchemaVersion, Description: payload.DocumentContent.Description, RuntimeConfig: payload.DocumentContent.RuntimeConfig, MainSteps: payload.DocumentContent.MainSteps, Parameters: payload.DocumentContent.Parameters, } return docparser.InitializeDocState(context, contracts.Association, docContent, documentInfo, parserInfo, payload.Parameters) } // newDocumentInfo initializes new DocumentInfo object func newDocumentInfo(rawData *model.InstanceAssociation, payload *messageContracts.SendCommandPayload) contracts.DocumentInfo { documentInfo := new(contracts.DocumentInfo) documentInfo.AssociationID = *(rawData.Association.AssociationId) documentInfo.InstanceID = *(rawData.Association.InstanceId) documentInfo.MessageID = fmt.Sprintf("aws.ssm.%v.%v", documentInfo.AssociationID, documentInfo.InstanceID) documentInfo.RunID = times.ToIsoDashUTC(time.Now()) documentInfo.DocumentID = *(rawData.Association.AssociationId) + "." + documentInfo.RunID rawData.DocumentID = documentInfo.DocumentID documentInfo.CreatedDate = times.ToIso8601UTC(rawData.CreateDate) documentInfo.DocumentName = payload.DocumentName documentInfo.DocumentVersion = *(rawData.Association.DocumentVersion) documentInfo.DocumentStatus = contracts.ResultStatusInProgress return *documentInfo }