Skip to content

Commit

Permalink
Populate metadata into PSResourceInfo object
Browse files Browse the repository at this point in the history
  • Loading branch information
alerickson committed Jan 30, 2024
1 parent 4b41e62 commit aa13a43
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 106 deletions.
266 changes: 172 additions & 94 deletions src/code/ACRServerAPICalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,32 +173,13 @@ public override FindResults FindName(string packageName, bool includePrerelease,
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
}

/* response returned looks something like:
* "registry": "myregistry.azurecr.io"
* "imageName": "hello-world"
* "tags": [
* {
* ""name"": ""1.0.0"",
* ""digest"": ""sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a"",
* ""createdTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""lastUpdateTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""signed"": false,
* ""changeableAttributes"": {
* ""deleteEnabled"": true,
* ""writeEnabled"": true,
* ""readEnabled"": true,
* ""listEnabled"": true
* }
* }]
*/
List<Hashtable> latestVersionResponse = new List<Hashtable>();
List<JToken> allVersionsList = foundTags["tags"].ToList();
allVersionsList.Reverse();

foreach (var packageVersion in allVersionsList)
foreach (var pkgVersionTagInfo in allVersionsList)
{
var packageVersionStr = packageVersion.ToString();
using (JsonDocument pkgVersionEntry = JsonDocument.Parse(packageVersionStr))
using (JsonDocument pkgVersionEntry = JsonDocument.Parse(pkgVersionTagInfo.ToString()))
{
JsonElement rootDom = pkgVersionEntry.RootElement;
if (!rootDom.TryGetProperty("name", out JsonElement pkgVersionElement))
Expand All @@ -218,7 +199,7 @@ public override FindResults FindName(string packageName, bool includePrerelease,
if (!pkgVersion.IsPrerelease || includePrerelease)
{
// Versions are always in descending order i.e 5.0.0, 3.0.0, 1.0.0 so grabbing the first match suffices
latestVersionResponse.Add(new Hashtable() { { packageName, packageVersionStr } });
latestVersionResponse.Add(GetACRMetadata(registry, packageName, pkgVersion, acrAccessToken, out errRecord));

break;
}
Expand Down Expand Up @@ -339,26 +320,9 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
}

/* response returned looks something like:
* "registry": "myregistry.azurecr.io"
* "imageName": "hello-world"
* "tags": [
* {
* ""name"": ""1.0.0"",
* ""digest"": ""sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a"",
* ""createdTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""lastUpdateTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""signed"": false,
* ""changeableAttributes"": {
* ""deleteEnabled"": true,
* ""writeEnabled"": true,
* ""readEnabled"": true,
* ""listEnabled"": true
* }
* }]
*/
List<Hashtable> latestVersionResponse = new List<Hashtable>();
List<JToken> allVersionsList = foundTags["tags"].ToList();
allVersionsList.Reverse();
foreach (var packageVersion in allVersionsList)
{
var packageVersionStr = packageVersion.ToString();
Expand Down Expand Up @@ -387,7 +351,7 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange
continue;
}

latestVersionResponse.Add(new Hashtable() { { packageName, packageVersionStr } });
latestVersionResponse.Add(GetACRMetadata(registry, packageName, pkgVersion, acrAccessToken, out errRecord));
}
}
}
Expand Down Expand Up @@ -453,59 +417,12 @@ public override FindResults FindVersion(string packageName, string version, Reso
}

_cmdletPassedIn.WriteVerbose("Getting tags");
var foundTags = FindAcrImageTags(registry, packageName, requiredVersion.ToString(), acrAccessToken, out errRecord);
if (errRecord != null || foundTags == null)
List<Hashtable> results = new List<Hashtable>
{
return new FindResults(stringResponse: new string[] { }, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
}

/* response returned looks something like:
* "registry": "myregistry.azurecr.io"
* "imageName": "hello-world"
* "tags": [
* {
* ""name"": ""1.0.0"",
* ""digest"": ""sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a"",
* ""createdTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""lastUpdateTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""signed"": false,
* ""changeableAttributes"": {
* ""deleteEnabled"": true,
* ""writeEnabled"": true,
* ""readEnabled"": true,
* ""listEnabled"": true
* }
* }]
*/
List<Hashtable> requiredVersionResponse = new List<Hashtable>();

var packageVersionStr = foundTags["tag"].ToString();
using (JsonDocument pkgVersionEntry = JsonDocument.Parse(packageVersionStr))
{
JsonElement rootDom = pkgVersionEntry.RootElement;
if (!rootDom.TryGetProperty("name", out JsonElement pkgVersionElement))
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain version element ('name') for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
}

if (NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
{
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");

if (pkgVersion == requiredVersion)
{
requiredVersionResponse.Add(new Hashtable() { { packageName, packageVersionStr } });
}
}
}
GetACRMetadata(registry, packageName, requiredVersion, acrAccessToken, out errRecord)
};

return new FindResults(stringResponse: new string[] { }, hashtableResponse: requiredVersionResponse.ToArray(), responseType: acrFindResponseType);
return new FindResults(stringResponse: new string[] { }, hashtableResponse: results.ToArray(), responseType: acrFindResponseType);
}

/// <summary>
Expand Down Expand Up @@ -709,6 +626,24 @@ internal async Task<HttpContent> GetAcrBlobAsync(string registry, string reposit

internal JObject FindAcrImageTags(string registry, string repositoryName, string version, string acrAccessToken, out ErrorRecord errRecord)
{
/* response returned looks something like:
* "registry": "myregistry.azurecr.io"
* "imageName": "hello-world"
* "tags": [
* {
* ""name"": ""1.0.0"",
* ""digest"": ""sha256:92c7f9c92844bbbb5d0a101b22f7c2a7949e40f8ea90c8b3bc396879d95e899a"",
* ""createdTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""lastUpdateTime"": ""2023-12-23T18:06:48.9975733Z"",
* ""signed"": false,
* ""changeableAttributes"": {
* ""deleteEnabled"": true,
* ""writeEnabled"": true,
* ""readEnabled"": true,
* ""listEnabled"": true
* }
* }]
*/
try
{
string resolvedVersion = string.Equals(version, "*", StringComparison.OrdinalIgnoreCase) ? null : $"/{version}";
Expand All @@ -722,6 +657,146 @@ internal JObject FindAcrImageTags(string registry, string repositoryName, string
}
}

internal Hashtable GetACRMetadata(string registry, string packageName, NuGetVersion requiredVersion, string acrAccessToken, out ErrorRecord errRecord)
{
Hashtable requiredVersionResponse = new Hashtable();

var foundTags = FindAcrManifest(registry, packageName, requiredVersion.ToNormalizedString(), acrAccessToken, out errRecord);
if (errRecord != null || foundTags == null)
{
return requiredVersionResponse;
}

/* Response returned looks something like:
* {
* "schemaVersion": 2,
* "config": {
* "mediaType": "application/vnd.unknown.config.v1+json",
* "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
* "size": 0
* },
* "layers": [
* {
* "mediaType": "application/vnd.oci.image.layer.nondistributable.v1.tar+gzip'",
* "digest": "sha256:7c55c7b66cb075628660d8249cc4866f16e34741c246a42ed97fb23ccd4ea956",
* "size": 3533,
* "annotations": {
* "org.opencontainers.image.title": "test_module.1.0.0.nupkg",
* "metadata": "{\"GUID\":\"45219bf4-10a4-4242-92d6-9bfcf79878fd\",\"FunctionsToExport\":[],\"CompanyName\":\"Anam\",\"CmdletsToExport\":[],\"VariablesToExport\":\"*\",\"Author\":\"Anam Navied\",\"ModuleVersion\":\"1.0.0\",\"Copyright\":\"(c) Anam Navied. All rights reserved.\",\"PrivateData\":{\"PSData\":{\"Tags\":[\"Test\",\"CommandsAndResource\",\"Tag2\"]}},\"RequiredModules\":[],\"Description\":\"This is a test module, for PSGallery team internal testing. Do not take a dependency on this package. This version contains tags for the package.\",\"AliasesToExport\":[]}"
* }
* }
* ]
* }
*/

var layers = foundTags["layers"];
if (layers == null || layers[0] == null)
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain 'layers' element in manifest for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}

var annotations = layers[0]["annotations"];
if (annotations == null)
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain 'annotations' element in manifest for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}

if (annotations["metadata"] == null)
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain 'metadata' element in manifest for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}

var metadata = annotations["metadata"].ToString();


var metadataPkgNameJToken = annotations["packageName"];
if (metadataPkgNameJToken == null)
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain 'packageName' element for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}

string metadataPkgName = metadataPkgNameJToken.ToString();
if (string.IsNullOrWhiteSpace(metadataPkgName))
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response element 'packageName' is empty for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}


using (JsonDocument metadataJSONDoc = JsonDocument.Parse(metadata))
{
JsonElement rootDom = metadataJSONDoc.RootElement;
if (!rootDom.TryGetProperty("ModuleVersion", out JsonElement pkgVersionElement) &&
!rootDom.TryGetProperty("Version", out pkgVersionElement))
{
errRecord = new ErrorRecord(
new InvalidOrEmptyResponse($"Response does not contain 'ModuleVersion' or 'Version' property in metadata for package '{packageName}' in '{Repository.Name}'."),
"FindNameFailure",
ErrorCategory.InvalidResult,
this);

return requiredVersionResponse;
}

if (NuGetVersion.TryParse(pkgVersionElement.ToString(), out NuGetVersion pkgVersion))
{
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");

if (pkgVersion == requiredVersion)
{
requiredVersionResponse.Add(metadataPkgName, metadata);
}
}
}

return requiredVersionResponse;
}

internal JObject FindAcrManifest(string registry, string packageName, string version, string acrAccessToken, out ErrorRecord errRecord)
{
try
{
var createManifestUrl = string.Format(acrManifestUrlTemplate, registry, packageName, version);
_cmdletPassedIn.WriteDebug($"GET manifest url: {createManifestUrl}");

var defaultHeaders = GetDefaultHeaders(acrAccessToken);
return GetHttpResponseJObjectUsingDefaultHeaders(createManifestUrl, HttpMethod.Get, defaultHeaders, out errRecord);
}
catch (HttpRequestException e)
{
throw new HttpRequestException("Error finding ACR manifest: " + e.Message);
}
}

internal async Task<string> GetStartUploadBlobLocation(string registry, string pkgName, string acrAccessToken)
{
try
Expand Down Expand Up @@ -1113,7 +1188,7 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
FileInfo nupkgFile = new FileInfo(fullNupkgFile);
var fileSize = nupkgFile.Length;
var fileName = System.IO.Path.GetFileName(fullNupkgFile);
string fileContent = CreateJsonContent(nupkgDigest, configDigest, fileSize, fileName, jsonString);
string fileContent = CreateJsonContent(nupkgDigest, configDigest, fileSize, fileName, pkgName, jsonString);
File.WriteAllText(configFilePath, fileContent);

_cmdletPassedIn.WriteVerbose("Create the manifest layer");
Expand All @@ -1130,7 +1205,8 @@ private string CreateJsonContent(
string nupkgDigest,
string configDigest,
long nupkgFileSize,
string fileName,
string fileName,
string packageName,
string jsonString)
{
StringBuilder stringBuilder = new StringBuilder();
Expand Down Expand Up @@ -1168,6 +1244,8 @@ private string CreateJsonContent(
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("org.opencontainers.image.title");
jsonWriter.WriteValue(fileName);
jsonWriter.WritePropertyName("packageName");
jsonWriter.WriteValue(packageName);
jsonWriter.WritePropertyName("metadata");
jsonWriter.WriteValue(jsonString);
jsonWriter.WriteEndObject();
Expand Down
Loading

0 comments on commit aa13a43

Please sign in to comment.