-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial code added for .NET Tool. TODO: Unit Tests, Integration Tests, MetaData.
- Loading branch information
1 parent
fb73112
commit e48518b
Showing
10 changed files
with
567 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
namespace Skyline.DataMiner.CICD.Tools.CatalogUpload.Lib | ||
{ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Logging; | ||
|
||
using Newtonsoft.Json; | ||
|
||
using Skyline.DataMiner.CICD.FileSystem; | ||
using Skyline.DataMiner.CICD.Tools.CatalogUpload.Lib.HttpArtifactUploadModels; | ||
|
||
/// <summary> | ||
/// Allows Uploading an artifact to the Catalog using one of the below in order of priority: | ||
/// <para>- provided key in upload argument (unix/win)</para> | ||
/// <para>- key stored as an Environment Variable called "dmcatalogkey". (unix/win)</para> | ||
/// <para>- key configured using Skyline.DataMiner.CICD.Tools.WinEncryptedKeys called "dmcatalogkey_encrypted" (windows only)</para> | ||
/// </summary> | ||
public class CatalogArtifact | ||
{ | ||
private readonly ILogger _logger; | ||
private readonly CatalogMetaData metaData; | ||
|
||
/// <summary> | ||
/// Creates an instance of <see cref="CatalogArtifact"/>. | ||
/// It searches for an optional dmcatalogkey in the "dmcatalogkey" or "dmcatalogkey_encrypted" Environment Variable. | ||
/// </summary> | ||
/// <param name="pathToArtifact">Path to the ".dmapp" or ".dmprotocol" file.</param> | ||
/// <param name="service">An instance of <see cref="ICatalogService"/> used for communication.</param> | ||
/// <param name="fileSystem">An instance of <see cref="IFileSystem"/> to access the filesystem. e.g. Skyline.DataMiner.CICD.FileSystem.Instance.</param> | ||
/// <param name="logger">An instance of <see cref="ILogger"/> that will hold error, debug and other information.</param> | ||
/// <param name="metaData">Contains package metadata.</param> | ||
public CatalogArtifact(string pathToArtifact, ICatalogService service, IFileSystem fileSystem, ILogger logger, CatalogMetaData metaData) | ||
{ | ||
this.metaData = metaData; | ||
_logger = logger; | ||
Fs = fileSystem; | ||
cancellationTokenSource = new CancellationTokenSource(); | ||
catalogService = service; | ||
PathToArtifact = pathToArtifact; | ||
TryFindEnvironmentKey(); | ||
} | ||
|
||
/// <summary> | ||
/// Creates an instance of <see cref="CatalogArtifact"/> using a default HttpCatalogService with a new HttpClient for communication. | ||
/// It searches for an optional dmcatalogkey in the "dmcatalogkey" or "dmcatalogkey_encrypted" Environment Variable for authentication. | ||
/// WARNING: when wishing to upload several Artifacts it's recommended to use the CatalogArtifact(string pathToArtifact, ICatalogService service, IFileSystem fileSystem, ILogger logger). | ||
/// </summary> | ||
/// <param name="pathToArtifact">Path to the ".dmapp" or ".dmprotocol" file.</param> | ||
/// <param name="logger">An instance of <see cref="ILogger"/> that will hold error, debug and other information.</param> | ||
/// <param name="metaData">Contains package metadata.</param> | ||
public CatalogArtifact(string pathToArtifact, ILogger logger, CatalogMetaData metaData) : this(pathToArtifact, new HttpCatalogService(new System.Net.Http.HttpClient(), logger), FileSystem.Instance, logger, metaData) | ||
{ | ||
|
||
} | ||
|
||
/// <summary> | ||
/// Path to the ".dmapp" or ".dmprotocol" file. | ||
/// </summary> | ||
public string PathToArtifact { get; private set; } | ||
|
||
private CancellationTokenSource cancellationTokenSource { get; set; } | ||
|
||
private ICatalogService catalogService { get; set; } | ||
|
||
private IFileSystem Fs { get; set; } | ||
|
||
private string keyFromEnv { get; set; } | ||
|
||
/// <summary> | ||
/// Cancels an ongoing upload. Create a new CatalogArtifact to attempt a new upload. | ||
/// </summary> | ||
public void CancelUpload() | ||
{ | ||
_logger.LogDebug($"Upload cancellation requested for {PathToArtifact}"); | ||
cancellationTokenSource.Cancel(); | ||
} | ||
|
||
/// <summary> | ||
/// Uploads to the private catalog using the provided dmcatalogkey. | ||
/// </summary> | ||
/// <param name="dmcatalogkey">A provided token for the agent or organization as defined in https://admin.dataminer.services/.</param> | ||
/// <returns>If the upload was successful or not.</returns> | ||
public async Task<ArtifactModel> UploadAsync(string dmcatalogkey) | ||
{ | ||
_logger.LogDebug($"Uploading {PathToArtifact}..."); | ||
|
||
byte[] packageData = Fs.File.ReadAllBytes(PathToArtifact); | ||
var result = await catalogService.ArtifactUploadAsync(packageData, dmcatalogkey, metaData, cancellationTokenSource.Token).ConfigureAwait(false); | ||
_logger.LogDebug($"Finished Uploading {PathToArtifact}"); | ||
|
||
_logger.LogInformation(JsonConvert.SerializeObject(result)); | ||
return result; | ||
} | ||
|
||
/// <summary> | ||
/// Uploads to the private catalog using the dmcatalogkey or dmcatalogkey_encrypted environment variable as the token. | ||
/// </summary> | ||
/// <returns>If the upload was successful or not.</returns> | ||
/// <exception cref="InvalidOperationException">Uploading failed.</exception> | ||
/// <exception cref="UnauthorizedAccessException">Uploading failed due to invalid Token.</exception> | ||
public async Task<ArtifactModel> UploadAsync() | ||
{ | ||
if (String.IsNullOrWhiteSpace(keyFromEnv)) | ||
{ | ||
throw new InvalidOperationException("Uploading failed, missing token in environment variable dmcatalogkey or dmcatalogkey_encrypted."); | ||
} | ||
|
||
_logger.LogDebug($"Attempting upload with Environment Variable as token for artifact: {PathToArtifact}..."); | ||
return await UploadAsync(keyFromEnv).ConfigureAwait(false); | ||
} | ||
|
||
/// <summary> | ||
/// Attempts to find the necessary API key in Environment Variables. In order of priority: | ||
/// <para>- key stored as an Environment Variable called "dmcatalogkey". (unix/win)</para> | ||
/// <para>- key configured using Skyline.DataMiner.CICD.Tools.WinEncryptedKeys called "dmcatalogkey_encrypted" (windows only)</para> | ||
/// </summary> | ||
private void TryFindEnvironmentKey() | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
var encryptedKey = WinEncryptedKeys.Lib.Keys.RetrieveKey("dmcatalogkey_encrypted"); | ||
if (encryptedKey != null) | ||
{ | ||
string keyFromWinEncryptedKeys = encryptedKey.ToString(); | ||
|
||
if (!String.IsNullOrWhiteSpace(keyFromWinEncryptedKeys)) | ||
{ | ||
_logger.LogDebug("OK: Found token in Env Variable: 'dmcatalogkey_encrypted' created by WinEncryptedKeys."); | ||
keyFromEnv = keyFromWinEncryptedKeys; | ||
} | ||
} | ||
} | ||
|
||
var config = new ConfigurationBuilder() | ||
.AddUserSecrets<CatalogArtifact>() | ||
.Build(); | ||
string keyFromEnvironment = config["dmcatalogkey"]; | ||
|
||
if (!String.IsNullOrWhiteSpace(keyFromEnvironment)) | ||
{ | ||
if (!String.IsNullOrWhiteSpace(keyFromEnv)) | ||
{ | ||
_logger.LogDebug("OK: Overriding 'dmcatalogkey_encrypted' with found token in Env Variable: 'dmcatalogkey'."); | ||
} | ||
else | ||
{ | ||
_logger.LogDebug("OK: Found token in Env Variable: 'dmcatalogkey'."); | ||
} | ||
|
||
keyFromEnv = keyFromEnvironment; | ||
} | ||
} | ||
} | ||
} |
169 changes: 169 additions & 0 deletions
169
CICD.Tools.CatalogUpload.Lib/CatalogService/CatalogMetaData.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
namespace Skyline.DataMiner.CICD.Tools.CatalogUpload.Lib | ||
{ | ||
using System; | ||
using System.IO; | ||
using System.IO.Compression; | ||
using System.Linq; | ||
using System.Xml.Linq; | ||
|
||
public class CatalogMetaData | ||
{ | ||
public string Branch { get; set; } = ""; | ||
|
||
public string CommitterMail { get; set; } = ""; | ||
|
||
public string ContentType { get; set; } = ""; | ||
|
||
public string Identifier { get; set; } = ""; | ||
|
||
public bool IsPreRelease { get; set; } | ||
|
||
public string Name { get; set; } = ""; | ||
|
||
public string ReleaseUri { get; set; } = ""; | ||
|
||
public string Version { get; set; } = ""; | ||
|
||
public static CatalogMetaData FromArtifact(string pathToArtifact) | ||
{ | ||
CatalogMetaData meta; | ||
|
||
if (String.IsNullOrWhiteSpace(pathToArtifact)) | ||
{ | ||
throw new ArgumentNullException(nameof(pathToArtifact)); | ||
} | ||
|
||
if (pathToArtifact.EndsWith(".dmapp", StringComparison.InvariantCultureIgnoreCase)) | ||
{ | ||
meta = CatalogMetaData.FromDmapp(pathToArtifact); | ||
} | ||
else if (pathToArtifact.EndsWith(".dmprotocol", StringComparison.InvariantCultureIgnoreCase)) | ||
{ | ||
meta = CatalogMetaData.FromDmprotocol(pathToArtifact); | ||
} | ||
else | ||
{ | ||
throw new InvalidOperationException($"Invalid path to artifact. Expected a path that ends with .dmapp or .dmprotocol but received {pathToArtifact}"); | ||
} | ||
|
||
return meta; | ||
} | ||
|
||
// Used during unit testing to assert data. | ||
public override bool Equals(object? obj) | ||
{ | ||
return obj is CatalogMetaData data && | ||
Version == data.Version && | ||
Branch == data.Branch && | ||
IsPreRelease == data.IsPreRelease && | ||
Identifier == data.Identifier && | ||
Name == data.Name && | ||
CommitterMail == data.CommitterMail && | ||
ReleaseUri == data.ReleaseUri && | ||
ContentType == data.ContentType; | ||
} | ||
|
||
// Needed to match with Equals | ||
public override int GetHashCode() | ||
{ | ||
return HashCode.Combine(Version, Branch, IsPreRelease, Identifier, Name, ContentType, CommitterMail, ReleaseUri); | ||
} | ||
|
||
private static CatalogMetaData FromDmapp(string pathToDmapp) | ||
{ | ||
// Open as a ZIP file. | ||
/*AppInfo.xml | ||
* | ||
* <?xml version="1.0" encoding="utf-8"?> | ||
<AppInfo> | ||
<DisplayName>COX Communications CISCO CBR-8 CCAP Platform Collector</DisplayName> | ||
<LastModifiedAt>2023-11-24T14:05:47</LastModifiedAt> | ||
<MinDmaVersion>10.0.9.0-9312</MinDmaVersion> | ||
<Name>COX Communications CISCO CBR-8 CCAP Platform Collector</Name> | ||
<AllowMultipleInstalledVersions>false</AllowMultipleInstalledVersions> | ||
<Version>0.0.0-CU1</Version> | ||
</AppInfo> | ||
* | ||
Description.txt | ||
Bridge Technologies VB Probe Series package version: 0.0.0-CU2 | ||
--------------------------------- | ||
Package creation time: 2023-11-24 13:38:17 | ||
--------------------------------- | ||
File Versions: | ||
Visio\skyline_Bridge Technologies VB Probe Series:0.0.0-CU2 | ||
*/ | ||
|
||
string appInfoRaw; | ||
|
||
using (var zipFile = ZipFile.OpenRead(pathToDmapp)) | ||
{ | ||
var foundFile = zipFile.Entries.FirstOrDefault(x => x.Name.Equals("AppInfo.xml", StringComparison.InvariantCulture)); | ||
if (foundFile == null) throw new InvalidOperationException("Could not find AppInfo.xml in the .dmapp."); | ||
|
||
using (var stream = foundFile.Open()) | ||
{ | ||
using (var memoryStream = new StreamReader(stream)) | ||
{ | ||
appInfoRaw = memoryStream.ReadToEnd(); | ||
} | ||
} | ||
} | ||
|
||
CatalogMetaData meta = new CatalogMetaData(); | ||
var appInfo = XDocument.Parse(appInfoRaw); | ||
meta.Name = appInfo.Element("DisplayName")?.Value; | ||
meta.Version = appInfo.Element("Version")?.Value; | ||
return meta; | ||
} | ||
|
||
private static CatalogMetaData FromDmprotocol(string pathToDmprotocol) | ||
{ | ||
// Description.txt | ||
/* | ||
Protocol Name: Microsoft Platform | ||
Protocol Version: 6.0.0.4_B2 | ||
* */ | ||
|
||
string descriptionText; | ||
|
||
using (var zipFile = ZipFile.OpenRead(pathToDmprotocol)) | ||
{ | ||
var foundFile = zipFile.Entries.FirstOrDefault(x => x.Name.Equals("Description.txt", StringComparison.InvariantCulture)); | ||
if (foundFile == null) throw new InvalidOperationException("Could not find Description.txt in the .dmapp."); | ||
|
||
using (var stream = foundFile.Open()) | ||
{ | ||
using (var memoryStream = new StreamReader(stream)) | ||
{ | ||
descriptionText = memoryStream.ReadToEnd(); | ||
} | ||
} | ||
} | ||
|
||
CatalogMetaData meta = new CatalogMetaData(); | ||
|
||
using(var reader = new StringReader(descriptionText)) | ||
{ | ||
var line = reader.ReadLine(); | ||
var splitLine = line.Split(':'); | ||
|
||
switch (splitLine[0]) | ||
{ | ||
case "Protocol Name": | ||
meta.Name = splitLine[1]; | ||
break; | ||
case "Protocol Version": | ||
meta.Version = splitLine[1]; | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
return meta; | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
CICD.Tools.CatalogUpload.Lib/CatalogService/CatalogServiceFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System.Net.Http; | ||
|
||
using Microsoft.Extensions.Logging; | ||
|
||
namespace Skyline.DataMiner.CICD.Tools.CatalogUpload.Lib.CatalogService | ||
{ | ||
/// <summary> | ||
/// Creates instances of <see cref="ICatalogService"/> to communicate with the Skyline DataMiner Catalog (https://catalog.dataminer.services/). | ||
/// </summary> | ||
public static class CatalogServiceFactory | ||
{ | ||
/// <summary> | ||
/// Creates instances of <see cref="ICatalogService"/> to communicate with the Skyline DataMiner Catalog (https://catalog.dataminer.services/) using HTTP for communication. | ||
/// </summary> | ||
/// <param name="httpClient">An instance of <see cref="HttpClient"/> used for communication with the catalog.</param> | ||
/// <param name="logger">An instance of <see cref="ILogger"/> for handling debug and error logging.</param> | ||
/// <returns>An instance of <see cref="ICatalogService"/> to communicate with the Skyline DataMiner Catalog (https://catalog.dataminer.services/).</returns> | ||
public static ICatalogService CreateWithHttp(HttpClient httpClient, ILogger logger) | ||
{ | ||
return new HttpCatalogService(httpClient, logger); | ||
} | ||
} | ||
} |
Oops, something went wrong.