using System; using System.Collections.Generic; using System.IO; using System.Text; using YamlDotNet.RepresentationModel; namespace Amazon.Lambda.Tools.TemplateProcessor { /// /// JSON implementation of ITemplateParser /// public class YamlTemplateParser : ITemplateParser { YamlStream Yaml { get; } public YamlTemplateParser(string templateBody) { var input = new StringReader(templateBody); // Load the stream this.Yaml = new YamlStream(); this.Yaml.Load(input); } public string GetUpdatedTemplate() { var myText = new StringWriter(); this.Yaml.Save(myText, assignAnchors: false); return myText.ToString(); } public IEnumerable UpdatableResources() { var root = (YamlMappingNode)this.Yaml.Documents[0].RootNode; if (root == null) throw new LambdaToolsException("CloudFormation template does not define any AWS resources", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingResourceSection); var resourcesKey = new YamlScalarNode("Resources"); if (!root.Children.ContainsKey(resourcesKey)) throw new LambdaToolsException("CloudFormation template does not define any AWS resources", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingResourceSection); var resources = (YamlMappingNode)root.Children[resourcesKey]; foreach (var resource in resources.Children) { var resourceBody = (YamlMappingNode)resource.Value; var type = (YamlScalarNode)resourceBody.Children[new YamlScalarNode("Type")]; if (!resourceBody.Children.ContainsKey("Properties")) continue; var properties = (YamlMappingNode)resourceBody.Children[new YamlScalarNode("Properties")]; if (properties == null) continue; if (type == null) continue; UpdatableResourceDefinition updatableResourceDefinition; if (!UpdatableResourceDefinition.ValidUpdatableResourceDefinitions.TryGetValue(type.Value, out updatableResourceDefinition)) continue; var updatableResource = new UpdatableResource(resource.Key.ToString(), updatableResourceDefinition, new YamlUpdatableResourceDataSource(root, resourceBody, properties)); yield return updatableResource; } } /// /// The JSON implementation of IUpdatableResourceDataSource /// public class YamlUpdatableResourceDataSource : IUpdatableResourceDataSource { YamlMappingNode Root { get; } YamlMappingNode Resource { get; } YamlMappingNode Properties { get; } public YamlUpdatableResourceDataSource(YamlMappingNode root, YamlMappingNode resource, YamlMappingNode properties) { this.Root = root; this.Resource = resource; this.Properties = properties; } public string GetValueFromRoot(params string[] keyPath) { return GetValue(this.Root, keyPath); } public string[] GetValueListFromRoot(params string[] keyPath) { return GetValueList(this.Root, keyPath); } public string GetValueFromResource(params string[] keyPath) { return GetValue(this.Resource, keyPath); } public string GetValue(params string[] keyPath) { return GetValue(this.Properties, keyPath); } private static string GetValue(YamlNode node, params string[] keyPath) { foreach (var key in keyPath) { if (node == null || !(node is YamlMappingNode)) return null; var mappingNode = ((YamlMappingNode)node); if (!mappingNode.Children.ContainsKey(key)) return null; node = mappingNode.Children[key]; } if (node is YamlScalarNode) { return ((YamlScalarNode)node).Value; } return null; } public string[] GetValueList(params string[] keyPath) { return GetValueList(this.Properties, keyPath); } public Dictionary GetValueDictionaryFromResource(params string[] keyPath) { return GetValueDictionaryFromResource(this.Resource, keyPath); } private static string[] GetValueList(YamlNode node, params string[] keyPath) { foreach (var key in keyPath) { if (node == null || !(node is YamlMappingNode)) return null; var mappingNode = ((YamlMappingNode)node); if (!mappingNode.Children.ContainsKey(key)) return null; node = mappingNode.Children[key]; } var sequenceNode = node as YamlSequenceNode; if (sequenceNode == null || sequenceNode.Children.Count == 0) { return null; } var values = new string[sequenceNode.Children.Count]; for (var i = 0; i < sequenceNode.Children.Count; i++) { values[i] = sequenceNode.Children[i]?.ToString(); } return values; } public void SetValue(string value, params string[] keyPath) { YamlMappingNode node = this.Properties; for (int i = 0; i < keyPath.Length - 1; i++) { var childNode = node.Children.ContainsKey(keyPath[i]) ? node[keyPath[i]] as YamlMappingNode : null; if (childNode == null) { childNode = new YamlMappingNode(); ((YamlMappingNode)node).Children.Add(keyPath[i], childNode); } node = childNode; } node.Children.Remove(keyPath[keyPath.Length - 1]); node.Children.Add(keyPath[keyPath.Length - 1], new YamlScalarNode(value)); } private static Dictionary GetValueDictionaryFromResource(YamlNode node, params string[] keyPath) { foreach (var key in keyPath) { if (node == null || !(node is YamlMappingNode)) return null; var mappingNode = ((YamlMappingNode)node); if (!mappingNode.Children.ContainsKey(key)) return null; node = mappingNode.Children[key]; } if (node is YamlMappingNode) { var mappingNode = (YamlMappingNode)node; if (mappingNode.Children.Count == 0) return null; Dictionary dictionary = new Dictionary(mappingNode.Children.Count); foreach (var key in mappingNode.Children.Keys) { if (dictionary.ContainsKey(key.ToString())) { dictionary[key.ToString()] = mappingNode.Children[key]?.ToString(); } else { dictionary.Add(key.ToString(), mappingNode.Children[key]?.ToString()); } } return dictionary; } return null; } } } }