/*******************************************************************************
* Copyright 2012-2018 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.
* *****************************************************************************
*
* AWS Tools for Windows (TM) PowerShell (TM)
*
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management.Automation;
using Amazon.Runtime;
namespace Amazon.PowerShell.Common
{
///
/// Execution history of AWS cmdlets, exposed as the $AWSHistory session variable.
///
public class AWSCmdletHistoryBuffer
{
static object _syncLock = new object();
const int DEFAULT_HISTORY_LENGTH = 5;
const int DEFAULT_SERVICECALL_HISTORY_LENGTH = 5;
static AWSCmdletHistoryBuffer _instance;
internal static AWSCmdletHistoryBuffer Instance
{
get
{
lock (_syncLock)
{
if (_instance == null)
_instance = new AWSCmdletHistoryBuffer();
return _instance;
}
}
}
HistoryBuffer _historyBuffer;
private HistoryBuffer History
{
get
{
HistoryBuffer historyBuffer = null;
lock (_syncLock)
historyBuffer = _historyBuffer;
return historyBuffer;
}
set
{
lock (_syncLock)
_historyBuffer = value;
}
}
internal AWSCmdletHistory StartCmdletHistory(string cmdletName)
{
return new AWSCmdletHistory(cmdletName);
}
int _historyLength = DEFAULT_HISTORY_LENGTH;
internal int MaxHistoryLength
{
get
{
lock (_syncLock)
{
return _historyLength;
}
}
set
{
lock (_syncLock)
{
if (value == _historyLength)
return;
if (value == 0)
{
History.Clear();
_historyLength = 0;
return;
}
History = new HistoryBuffer(value, History);
_historyLength = value;
}
}
}
int _serviceCallHistoryLength = DEFAULT_SERVICECALL_HISTORY_LENGTH;
internal int ServiceCallHistoryLength
{
get { return _serviceCallHistoryLength; }
set
{
if (value <= 0)
throw new ArgumentOutOfRangeException();
// might want to ripple down existing buffer and update existing service
// call instances here
_serviceCallHistoryLength = value;
}
}
public AWSCmdletHistory this[int index]
{
get
{
if (!RecordingEnabled)
return null;
if (index >= 0 && index < MaxHistoryLength)
{
lock (_syncLock)
{
return History.ElementAt(index);
}
}
else
throw new ArgumentOutOfRangeException();
}
}
internal void Clear()
{
lock (_syncLock)
{
History.Clear();
}
}
internal void Save(string fileName)
{
}
public override string ToString()
{
if (!RecordingEnabled)
return "AWS History Disabled";
return string.Format("{0} command(s) recorded{1}.",
this.History.Count,
LastCommand != null
? ", last = " + LastCommand
: "");
}
bool _recordServiceRequests = false;
internal bool RecordServiceRequests
{
get
{
lock (_syncLock)
{
return _recordServiceRequests;
}
}
set
{
lock (_syncLock)
{
_recordServiceRequests = value;
}
}
}
bool _includeSensitiveData = false;
internal bool IncludeSensitiveData
{
get
{
lock (_syncLock)
{
return _includeSensitiveData ;
}
}
set
{
lock (_syncLock)
{
_includeSensitiveData = value;
}
}
}
private bool RecordingEnabled
{
get
{
int historyLength;
lock (_syncLock)
historyLength = _historyLength;
return historyLength != 0;
}
}
internal void PushCmdletHistory(AWSCmdletHistory cmdletHistory)
{
if (RecordingEnabled)
{
cmdletHistory.CmdletEnd = DateTime.Now;
History.Add(cmdletHistory);
}
}
///
/// Exposes the inner command trace to the shell
///
public IEnumerable Commands
{
get { return History.History; }
}
///
/// Helper property to access the very last cmdlet that was run
///
public AWSCmdletHistory LastCommand
{
get
{
lock (_syncLock)
{
return History.Last;
}
}
}
///
/// Helper property to access the very last service response that was recorded on
/// the last invocation.
///
public object LastServiceResponse
{
get
{
lock (_syncLock)
{
var lastInvoke = LastCommand;
if (lastInvoke != null)
return lastInvoke.LastServiceResponse;
}
return null;
}
}
///
/// Helper property to access the very last service response that was recorded on
/// the last invocation.
///
public object LastServiceRequest
{
get
{
lock (_syncLock)
{
var lastInvoke = LastCommand;
if (lastInvoke != null)
return lastInvoke.LastServiceRequest;
}
return null;
}
}
private AWSCmdletHistoryBuffer()
{
History = new HistoryBuffer(_historyLength);
}
}
///
/// Records the responses and optionally requests that the cmdlet made, together
/// with other diagnostic info.
///
public class AWSCmdletHistory
{
readonly object _syncLock = new object();
public string CmdletName { get; private set; }
public DateTime CmdletStart { get; private set; }
public DateTime CmdletEnd { get; internal set; }
public int EmittedObjectsCount { get; internal set; }
private AWSCmdletHistory() { }
public HistoryBuffer Requests { get; private set; }
public HistoryBuffer Responses { get; private set; }
// indexer returns composed object with both request and response
public PSObject this[int index]
{
get
{
if (index >= 0 && index < AWSCmdletHistoryBuffer.Instance.ServiceCallHistoryLength)
{
lock (_syncLock)
{
var pso = new PSObject();
PSObject requestObj = null;
if (Requests.Count != 0)
requestObj = new PSObject(Requests.ElementAt(index));
PSObject responseObj = null;
if (Responses.Count != 0)
responseObj = new PSObject(Responses.ElementAt(index));
pso.Properties.Add(new PSNoteProperty("Request", requestObj));
pso.Properties.Add(new PSNoteProperty("Response", responseObj));
return pso;
}
}
else
throw new ArgumentOutOfRangeException();
}
}
///
/// Helper property to access the very last service response that was recorded
///
public PSObject LastServiceResponse
{
get
{
lock (_syncLock)
{
return Responses.Last;
}
}
}
///
/// Helper property to access the very last service request that was recorded,
/// if request logging is turned on.
///
public PSObject LastServiceRequest
{
get
{
lock (_syncLock)
{
return Requests.Last;
}
}
}
public override string ToString()
{
return this.CmdletName;
}
internal AWSCmdletHistory(string cmdletName)
{
this.CmdletName = cmdletName;
Requests = new HistoryBuffer(AWSCmdletHistoryBuffer.Instance.ServiceCallHistoryLength);
Responses = new HistoryBuffer(AWSCmdletHistoryBuffer.Instance.ServiceCallHistoryLength);
this.EmittedObjectsCount = 0;
this.CmdletEnd = this.CmdletStart = DateTime.Now;
}
public void PushServiceRequest(AmazonWebServiceRequest request, InvocationInfo invocationInfo, bool isSensitiveRequest)
{
if (!AWSCmdletHistoryBuffer.Instance.RecordServiceRequests || (isSensitiveRequest && !AWSCmdletHistoryBuffer.Instance.IncludeSensitiveData))
return;
var pso = new PSObject(request);
if (invocationInfo != null)
pso.Properties.Add(new PSNoteProperty("InvocationLine", invocationInfo.Line));
pso.Properties.Add(new PSNoteProperty("LoggedAt", DateTime.Now));
lock (_syncLock)
{
Requests.Add(pso);
}
}
internal void PushServiceRequest(AmazonWebServiceRequest request, Dictionary noteProperties)
{
if (!AWSCmdletHistoryBuffer.Instance.RecordServiceRequests)
return;
var pso = new PSObject(request);
if (noteProperties != null)
{
foreach (var kvp in noteProperties)
{
pso.Properties.Add(new PSNoteProperty(kvp.Key, kvp.Value));
}
}
pso.Properties.Add(new PSNoteProperty("LoggedAt", DateTime.Now));
lock (_syncLock)
{
Requests.Add(pso);
}
}
internal void PushServiceResponse(object responseOrError, bool isSensitiveResponse)
{
if (isSensitiveResponse && !AWSCmdletHistoryBuffer.Instance.IncludeSensitiveData)
return;
this.PushServiceResponse(responseOrError, null);
}
internal void PushServiceResponse(object responseOrError, Dictionary noteProperties)
{
if (responseOrError == null && (noteProperties == null || noteProperties.Count == 0))
return;
var pso = responseOrError != null ? new PSObject(responseOrError) : new PSObject();
if (noteProperties != null)
{
foreach (var kvp in noteProperties)
{
pso.Properties.Add(new PSNoteProperty(kvp.Key, kvp.Value));
}
}
pso.Properties.Add(new PSNoteProperty("LoggedAt", DateTime.Now));
lock (_syncLock)
{
Responses.Add(pso);
}
}
}
///
/// Simplistic fixed-size buffer implementation that can be used to
/// keep track of cmdlet exec history and the request/response calls
/// that each cmdlet makes.
///
///
public class HistoryBuffer
{
readonly Queue _queue;
readonly int _maxLength;
public HistoryBuffer(int maxLength)
{
_queue = new Queue(maxLength);
_maxLength = maxLength;
}
public HistoryBuffer(int maxLength, HistoryBuffer currentBuffer)
{
_queue = new Queue(maxLength);
int oldestToCopy = maxLength > currentBuffer.Count ? 0 : currentBuffer.Count - maxLength;
for (int i = oldestToCopy; i < currentBuffer.Count; i++)
{
_queue.Enqueue(currentBuffer._queue.ElementAt(i));
}
_maxLength = maxLength;
}
public IEnumerable History
{
get { return _queue; }
}
public void Add(T obj)
{
if (_queue.Count == _maxLength)
{
_queue.Dequeue();
_queue.Enqueue(obj);
}
else
_queue.Enqueue(obj);
}
public T Read()
{
return _queue.Dequeue();
}
public void Clear()
{
_queue.Clear();
}
public T ElementAt(int index)
{
return _queue.ElementAtOrDefault(index);
}
public T Last
{
get
{
return _queue.LastOrDefault();
}
}
public int Count
{
get { return _queue.Count; }
}
public T this[int index]
{
get
{
return this.ElementAt(index);
}
}
HistoryBuffer() {}
}
}