Skip to content

Commit

Permalink
feat: add support for remote management, ephemeral and api url in Win…
Browse files Browse the repository at this point in the history
…dows installer
  • Loading branch information
fguimond committed Feb 13, 2024
1 parent d2d238d commit 378537f
Show file tree
Hide file tree
Showing 9 changed files with 1,190 additions and 771 deletions.
120 changes: 62 additions & 58 deletions msi/SumoLogic.wixext/SumoLogic.wixext/Config.cs
Original file line number Diff line number Diff line change
@@ -1,58 +1,62 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace SumoLogic.wixext
{
public class Config
{
public string InstallationToken { get; set; }
public Dictionary<string, string> CollectorFields { get; set; }

public Config() {
this.CollectorFields = new Dictionary<string, string>();
}

public void SetCollectorFieldsFromTags(string tags)
{
if (tags.Length == 0) { return; }

var tagsRx = new Regex(@"([^=,]+)=([^\0]+?)(?=,[^,]+=|$)", RegexOptions.Compiled);
var matches = tagsRx.Matches(tags);

if (matches.Count == 0)
{
throw new TagsSyntaxException("tags were provided with invalid syntax");
}
if (matches.Count > 10)
{
throw new TagsLimitExceededException("the limit of 10 tags was exceeded");
}

foreach (Match match in matches)
{
if (match.Groups.Count != 3)
{
Console.WriteLine("Groups: {0}", match.Groups.Count);
var msg = string.Format("invalid syntax for tag: {0}", match.Value);
throw new TagSyntaxException(msg);
}
var key = match.Groups[1].Value.Trim();
var value = match.Groups[2].Value.Trim();

if (key.Length > 255)
{
var msg = string.Format("tag key exceeds maximum length of 255: {0}", key);
throw new TagKeyLengthExceededException(msg);
}
if (value.Length > 200)
{
var msg = string.Format("tag value exceeds maximum length of 200: {0}", value);
throw new TagValueLengthExceededException(msg);
}

this.CollectorFields.Add(key, value);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace SumoLogic.wixext
{
public class Config
{
public string InstallationToken { get; set; }
public Dictionary<string, string> CollectorFields { get; set; }
public bool RemotelyManaged { get; set; }
public bool Ephemeral { get; set; }
public string OpAmpFolder { get; set; }
public string Api { get; set; }

public Config() {
this.CollectorFields = new Dictionary<string, string>();
}

public void SetCollectorFieldsFromTags(string tags)
{
if (tags.Length == 0) { return; }

var tagsRx = new Regex(@"([^=,]+)=([^\0]+?)(?=,[^,]+=|$)", RegexOptions.Compiled);
var matches = tagsRx.Matches(tags);

if (matches.Count == 0)
{
throw new TagsSyntaxException("tags were provided with invalid syntax");
}
if (matches.Count > 10)
{
throw new TagsLimitExceededException("the limit of 10 tags was exceeded");
}

foreach (Match match in matches)
{
if (match.Groups.Count != 3)
{
Console.WriteLine("Groups: {0}", match.Groups.Count);
var msg = string.Format("invalid syntax for tag: {0}", match.Value);
throw new TagSyntaxException(msg);
}
var key = match.Groups[1].Value.Trim();
var value = match.Groups[2].Value.Trim();

if (key.Length > 255)
{
var msg = string.Format("tag key exceeds maximum length of 255: {0}", key);
throw new TagKeyLengthExceededException(msg);
}
if (value.Length > 200)
{
var msg = string.Format("tag value exceeds maximum length of 200: {0}", value);
throw new TagValueLengthExceededException(msg);
}

this.CollectorFields.Add(key, value);
}
}
}
}
272 changes: 168 additions & 104 deletions msi/SumoLogic.wixext/SumoLogic.wixext/ConfigUpdater.cs
Original file line number Diff line number Diff line change
@@ -1,104 +1,168 @@
using System.Collections.Generic;
using System.IO;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;

namespace SumoLogic.wixext
{
public class ConfigUpdater
{
public YamlDocument Document { get; set; }

public ConfigUpdater(StreamReader streamReader) {
try
{
var ys = new YamlStream();
ys.Load(streamReader);
if (ys.Documents.Count == 0)
{
throw new EmptyConfigException("config file is empty");
}
this.Document = ys.Documents[0];
}
finally
{
streamReader.Dispose();
}
}

public void Update(Config config)
{
YamlMappingNode root = (YamlMappingNode)this.Document.RootNode;

EnsureMapKey(root, "extensions");
YamlMappingNode extensions = (YamlMappingNode)root.Children["extensions"];

EnsureMapKey(extensions, "sumologic");
YamlMappingNode sumologic = (YamlMappingNode)extensions.Children["sumologic"];

if (config.InstallationToken != "")
{
EnsureScalarKey(sumologic, "installation_token");
sumologic.Children["installation_token"] = config.InstallationToken;
}

if (config.CollectorFields.Count > 0)
{
EnsureMapKey(sumologic, "collector_fields");
YamlMappingNode collectorFields = (YamlMappingNode)sumologic.Children["collector_fields"];

foreach (KeyValuePair<string, string> field in config.CollectorFields)
{
EnsureScalarKey(collectorFields, field.Key);
collectorFields.Children[field.Key] = field.Value;
}
}
}

public void Save(StreamWriter streamWriter)
{
try
{
var serializer = new Serializer();
serializer.Serialize(streamWriter, this.Document.RootNode);
}
finally
{
streamWriter.Flush();
}
}

private void EnsureMapKey(YamlMappingNode node, string key)
{
if (node.Children.ContainsKey(key))
{
if (node.Children[key].NodeType == YamlNodeType.Mapping) {
return;
}

// TODO: is this how we want to handle incorrect node types?
// YamlNode is wrong type, remove it
node.Children.Remove(key);
}
// Add empty YamlMappingNode to key
node.Children.Add(key, new YamlMappingNode());
}

private void EnsureScalarKey(YamlMappingNode node, string key)
{
if (node.Children.ContainsKey(key))
{
if (node.Children[key].NodeType == YamlNodeType.Scalar)
{
return;
}

// TODO: is this how we want to handle incorrect node types?
// YamlNode is wrong type, remove it
node.Children.Remove(key);
}
// Add empty YamlScalarNode to key
node.Children.Add(key, new YamlScalarNode());
}
}
}
using System.Collections.Generic;
using System.IO;
using YamlDotNet.RepresentationModel;
using YamlDotNet.Serialization;

namespace SumoLogic.wixext
{
public class ConfigUpdater
{
public YamlDocument Document { get; set; }

public ConfigUpdater(StreamReader streamReader) {
try
{
var ys = new YamlStream();
ys.Load(streamReader);
if (ys.Documents.Count == 0)
{
throw new EmptyConfigException("config file is empty");
}
this.Document = ys.Documents[0];
}
finally
{
streamReader.Dispose();
}
}

public void Update(Config config)
{
if (config.RemotelyManaged && string.IsNullOrEmpty(config.OpAmpFolder))
{
throw new MissingConfigurationException("OpAmpFolder");
}

YamlMappingNode root = (YamlMappingNode)this.Document.RootNode;

EnsureMapKey(root, "extensions");
YamlMappingNode extensions = (YamlMappingNode)root.Children["extensions"];

EnsureMapKey(extensions, "sumologic");
YamlMappingNode sumologic = (YamlMappingNode)extensions.Children["sumologic"];

if (config.InstallationToken != "")
{
EnsureScalarKey(sumologic, "installation_token");
sumologic.Children["installation_token"] = config.InstallationToken;
}

if (config.CollectorFields.Count > 0)
{
EnsureMapKey(sumologic, "collector_fields");
YamlMappingNode collectorFields = (YamlMappingNode)sumologic.Children["collector_fields"];

foreach (KeyValuePair<string, string> field in config.CollectorFields)
{
EnsureScalarKey(collectorFields, field.Key);
collectorFields.Children[field.Key] = field.Value;
}
}

if (config.RemotelyManaged)
{
EnsureMapKey(extensions, "opamp");
YamlMappingNode opamp = (YamlMappingNode)extensions.Children["opamp"];
EnsureScalarKey(opamp, "remote_configuration_directory");
opamp.Children["remote_configuration_directory"] = config.OpAmpFolder;

// Add OpAmp extension to service section
EnsureMapKey(root, "service");
YamlMappingNode service = (YamlMappingNode)root.Children["service"];
EnsureSequenceKey(service, "extensions");
YamlSequenceNode serviceExtensions = (YamlSequenceNode)service.Children["extensions"];
if (!serviceExtensions.Children.Contains("opamp"))
{
serviceExtensions.Children.Add("opamp");
}
}

if (config.Ephemeral)
{
EnsureScalarKey(sumologic, "ephemeral");
sumologic.Children["ephemeral"] = "true";
}

if (!string.IsNullOrEmpty(config.Api))
{
EnsureScalarKey(sumologic, "api_base_url");
sumologic.Children["api_base_url"] = config.Api;
}

// Make sure the sumologic processor node is a map node, otherwise an empty string
// is generated as the value instead of an empty node.
if (root.Children.ContainsKey("processors"))
{
EnsureMapKey(root, "processors");
YamlMappingNode processors = (YamlMappingNode)root.Children["processors"];
if (processors.Children.ContainsKey("sumologic"))
{
EnsureMapKey(processors, "sumologic");
}
}
}

public void Save(StreamWriter streamWriter)
{
try
{
var serializer = new Serializer();
serializer.Serialize(streamWriter, this.Document.RootNode);
}
finally
{
streamWriter.Flush();
}
}

private void EnsureMapKey(YamlMappingNode node, string key)
{
if (node.Children.ContainsKey(key))
{
if (node.Children[key].NodeType == YamlNodeType.Mapping) {
return;
}

// TODO: is this how we want to handle incorrect node types?
// YamlNode is wrong type, remove it
node.Children.Remove(key);
}
// Add empty YamlMappingNode to key
node.Children.Add(key, new YamlMappingNode());
}

private void EnsureScalarKey(YamlMappingNode node, string key)
{
if (node.Children.ContainsKey(key))
{
if (node.Children[key].NodeType == YamlNodeType.Scalar)
{
return;
}

// TODO: is this how we want to handle incorrect node types?
// YamlNode is wrong type, remove it
node.Children.Remove(key);
}
// Add empty YamlScalarNode to key
node.Children.Add(key, new YamlScalarNode());
}

private void EnsureSequenceKey(YamlMappingNode node, string key)
{
if (node.Children.ContainsKey(key))
{
if (node.Children[key].NodeType == YamlNodeType.Sequence)
{
return;
}

// TODO: is this how we want to handle incorrect node types?
// YamlNode is wrong type, remove it
node.Children.Remove(key);
}
// Add empty YamlScalarNode to key
node.Children.Add(key, new YamlSequenceNode());
}
}
}
Loading

0 comments on commit 378537f

Please sign in to comment.