Skip to content

Commit c353ebe

Browse files
authored
Add support for Find/Install ACR packages with dependencies (#1587)
1 parent 868c96d commit c353ebe

File tree

5 files changed

+114
-350
lines changed

5 files changed

+114
-350
lines changed

src/code/ACRServerAPICalls.cs

Lines changed: 5 additions & 330 deletions
Original file line numberDiff line numberDiff line change
@@ -741,20 +741,6 @@ internal async Task<HttpResponseMessage> UploadManifest(string pkgName, string p
741741
}
742742
}
743743

744-
internal async Task<HttpResponseMessage> UploadDependencyManifest(string pkgName, string referenceSHA, string configPath, bool isManifest, string acrAccessToken)
745-
{
746-
try
747-
{
748-
var createManifestUrl = string.Format(acrManifestUrlTemplate, Registry, pkgName, referenceSHA);
749-
var defaultHeaders = GetDefaultHeaders(acrAccessToken);
750-
return await PutRequestAsync(createManifestUrl, configPath, isManifest, defaultHeaders);
751-
}
752-
catch (HttpRequestException e)
753-
{
754-
throw new HttpRequestException("Error occured while trying to create manifest: " + e.Message);
755-
}
756-
}
757-
758744
internal async Task<HttpContent> GetHttpContentResponseJObject(string url, Collection<KeyValuePair<string, string>> defaultHeaders)
759745
{
760746
try
@@ -1039,15 +1025,9 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
10391025
_cmdletPassedIn.ThrowTerminatingError(metadataCreationError);
10401026
}
10411027

1042-
// Create and upload manifest
1043-
TryCreateAndUploadManifest(fullNupkgFile, nupkgDigest, configDigest, pkgName, resourceType, metadataJson, configFilePath,
1044-
pkgNameLower, pkgVersion, acrAccessToken, out HttpResponseMessage manifestResponse);
1045-
1046-
// After manifest is created, see if there are any dependencies that need to be tracked on the server
1047-
if (dependencies != null && dependencies.Count > 0)
1048-
{
1049-
TryProcessDependencies(dependencies, pkgNameLower, acrAccessToken, manifestResponse);
1050-
}
1028+
// Create and upload manifest
1029+
TryCreateAndUploadManifest(fullNupkgFile, nupkgDigest, configDigest, pkgName, resourceType, metadataJson, configFilePath,
1030+
pkgNameLower, pkgVersion, acrAccessToken);
10511031

10521032
return true;
10531033
}
@@ -1117,7 +1097,7 @@ private bool TryCreateConfig(string configFilePath, out string configDigest)
11171097
}
11181098

11191099
private bool TryCreateAndUploadManifest(string fullNupkgFile, string nupkgDigest, string configDigest, string pkgName, ResourceType resourceType, string metadataJson, string configFilePath,
1120-
string pkgNameLower, NuGetVersion pkgVersion, string acrAccessToken, out HttpResponseMessage manifestResponse)
1100+
string pkgNameLower, NuGetVersion pkgVersion, string acrAccessToken)
11211101
{
11221102
FileInfo nupkgFile = new FileInfo(fullNupkgFile);
11231103
var fileSize = nupkgFile.Length;
@@ -1126,7 +1106,7 @@ private bool TryCreateAndUploadManifest(string fullNupkgFile, string nupkgDigest
11261106
File.WriteAllText(configFilePath, fileContent);
11271107

11281108
_cmdletPassedIn.WriteVerbose("Create the manifest layer");
1129-
manifestResponse = UploadManifest(pkgNameLower, pkgVersion.OriginalVersion, configFilePath, true, acrAccessToken).Result;
1109+
HttpResponseMessage manifestResponse = UploadManifest(pkgNameLower, pkgVersion.OriginalVersion, configFilePath, true, acrAccessToken).Result;
11301110
bool manifestCreated = manifestResponse.IsSuccessStatusCode;
11311111
if (!manifestCreated)
11321112
{
@@ -1141,175 +1121,6 @@ private bool TryCreateAndUploadManifest(string fullNupkgFile, string nupkgDigest
11411121
return manifestCreated;
11421122
}
11431123

1144-
private bool TryProcessDependencies(Hashtable dependencies, string pkgNameLower, string acrAccessToken, HttpResponseMessage manifestResponse)
1145-
{
1146-
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
1147-
1148-
try
1149-
{
1150-
Directory.CreateDirectory(tempPath);
1151-
1152-
// Create dependency.json
1153-
TryCreateAndUploadDependencyJson(tempPath, dependencies, pkgNameLower, acrAccessToken, out string depJsonContent, out string depDigest, out long depFileSize, out string depFileName);
1154-
1155-
// Create and upload an empty file-- needed by ACR server
1156-
if (!TryCreateAndUploadEmptyTxtFile(tempPath, pkgNameLower, acrAccessToken))
1157-
{
1158-
return false;
1159-
}
1160-
1161-
// Create artifactconfig.json file
1162-
string depConfigFileName = "artifactconfig.json";
1163-
var depConfigFilePath = System.IO.Path.Combine(tempPath, depConfigFileName);
1164-
while (File.Exists(depConfigFilePath))
1165-
{
1166-
depConfigFilePath = System.IO.Path.Combine(tempPath, Guid.NewGuid().ToString() + ".json");
1167-
}
1168-
if (!TryCreateDependencyConfigFile(depConfigFilePath, manifestResponse, depJsonContent, depDigest, depFileSize, depFileName, out string artifactDigest))
1169-
{
1170-
return false;
1171-
}
1172-
1173-
_cmdletPassedIn.WriteVerbose("Create the manifest");
1174-
1175-
// Upload Manifest
1176-
var depManifestResponse = UploadDependencyManifest(pkgNameLower, $"sha256:{artifactDigest}", depConfigFilePath, true, acrAccessToken).Result;
1177-
bool depManifestCreated = depManifestResponse.IsSuccessStatusCode;
1178-
if (!depManifestCreated)
1179-
{
1180-
_cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
1181-
new ArgumentException("Error uploading dependency manifest"),
1182-
"DependencyManifestUploadError",
1183-
ErrorCategory.InvalidResult,
1184-
_cmdletPassedIn));
1185-
return false;
1186-
}
1187-
1188-
_cmdletPassedIn.WriteVerbose("End of dependency processing");
1189-
}
1190-
catch (Exception e)
1191-
{
1192-
throw new ProcessDependencyException("Error processing dependencies: " + e.Message);
1193-
}
1194-
finally
1195-
{
1196-
if (Directory.Exists(tempPath))
1197-
{
1198-
// Delete the temp directory and all its contents
1199-
_cmdletPassedIn.WriteVerbose($"Attempting to delete '{tempPath}'");
1200-
Utils.DeleteDirectoryWithRestore(tempPath);
1201-
}
1202-
}
1203-
1204-
return true;
1205-
}
1206-
1207-
private bool TryCreateAndUploadDependencyJson(string tempPath,
1208-
Hashtable dependencies, string pkgNameLower, string acrAccessToken, out string depJsonContent, out string depDigest, out long depFileSize, out string depFileName)
1209-
{
1210-
depFileName = "dependency.json";
1211-
var depFilePath = System.IO.Path.Combine(tempPath, depFileName);
1212-
Utils.CreateFile(depFilePath);
1213-
FileInfo depFile = new FileInfo(depFilePath);
1214-
1215-
depJsonContent = CreateDependencyJsonContent(dependencies);
1216-
File.WriteAllText(depFilePath, depJsonContent);
1217-
depFileSize = depFile.Length;
1218-
1219-
bool depDigestCreated = CreateDigest(depFilePath, out depDigest, out ErrorRecord depDigestError);
1220-
if (depDigestError != null)
1221-
{
1222-
_cmdletPassedIn.ThrowTerminatingError(depDigestError);
1223-
}
1224-
1225-
// Upload dependency.json
1226-
var depLocation = GetStartUploadBlobLocation(pkgNameLower, acrAccessToken).Result;
1227-
var depFileResponse = EndUploadBlob(depLocation, depFilePath, depDigest, isManifest: false, acrAccessToken).Result;
1228-
1229-
return depFileResponse.IsSuccessStatusCode;
1230-
}
1231-
1232-
private bool TryCreateAndUploadEmptyTxtFile(string tempPath, string pkgNameLower, string acrAccessToken)
1233-
{
1234-
_cmdletPassedIn.WriteVerbose("Create an empty artifact file");
1235-
string emptyArtifactFileName = "artifactEmpty.txt";
1236-
var emptyArtifactFilePath = System.IO.Path.Combine(tempPath, emptyArtifactFileName);
1237-
// Rename the empty file in case such a file already exists in the temp folder (although highly unlikely)
1238-
while (File.Exists(emptyArtifactFilePath))
1239-
{
1240-
emptyArtifactFilePath = Guid.NewGuid().ToString() + ".txt";
1241-
}
1242-
Utils.CreateFile(emptyArtifactFilePath);
1243-
1244-
_cmdletPassedIn.WriteVerbose("Start uploading an empty artifact file");
1245-
var emptyArtifactLocation = GetStartUploadBlobLocation(pkgNameLower, acrAccessToken).Result;
1246-
_cmdletPassedIn.WriteVerbose("Computing digest for empty file");
1247-
bool emptyArtifactDigestCreated = CreateDigest(emptyArtifactFilePath, out string emptyArtifactDigest, out ErrorRecord emptyArtifactDigestError);
1248-
if (!emptyArtifactDigestCreated)
1249-
{
1250-
_cmdletPassedIn.ThrowTerminatingError(emptyArtifactDigestError);
1251-
}
1252-
_cmdletPassedIn.WriteVerbose("Finish uploading empty file");
1253-
var emptyArtifactResponse = EndUploadBlob(emptyArtifactLocation, emptyArtifactFilePath, emptyArtifactDigest, false, acrAccessToken).Result;
1254-
1255-
return emptyArtifactResponse.IsSuccessStatusCode;
1256-
}
1257-
1258-
private bool TryCreateDependencyConfigFile(string depConfigFilePath, HttpResponseMessage manifestResponse, string depJsonContent, string depDigest, long depFileSize,
1259-
string depFileName, out string artifactDigest)
1260-
{
1261-
artifactDigest = string.Empty;
1262-
_cmdletPassedIn.WriteVerbose("Create the dependency config file");
1263-
Utils.CreateFile(depConfigFilePath);
1264-
1265-
_cmdletPassedIn.WriteVerbose("Computing digest for artifact config");
1266-
bool depConfigDigestCreated = CreateDigest(depConfigFilePath, out string emptyConfigArtifactDigest, out ErrorRecord depConfigDigestError);
1267-
if (!depConfigDigestCreated)
1268-
{
1269-
_cmdletPassedIn.ThrowTerminatingError(depConfigDigestError);
1270-
}
1271-
FileInfo depConfigFile = new FileInfo(depConfigFilePath);
1272-
1273-
1274-
// Can either get the digest/size through response from pushing parent manifest earlier,
1275-
string[] parentLocation = manifestResponse.Headers.Location.OriginalString.Split(':');
1276-
if (parentLocation == null && parentLocation.Length < 2)
1277-
{
1278-
_cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
1279-
new ArgumentException("Error creating dependency manifest. Parent manifest location is invalid."),
1280-
"DependencyManifestCreationError",
1281-
ErrorCategory.InvalidResult,
1282-
_cmdletPassedIn));
1283-
return false;
1284-
}
1285-
1286-
string parentDigest = parentLocation[1];
1287-
var contentLength = manifestResponse.RequestMessage.Content.Headers.ContentLength;
1288-
if (contentLength == null)
1289-
{
1290-
_cmdletPassedIn.ThrowTerminatingError(new ErrorRecord(
1291-
new ArgumentException("Error creating dependency manifest. Parent manifest size is invalid."),
1292-
"DependencyManifestCreationError",
1293-
ErrorCategory.InvalidResult,
1294-
_cmdletPassedIn));
1295-
}
1296-
long parentSize = (long)manifestResponse.RequestMessage.Content.Headers.ContentLength;
1297-
1298-
// Create manifest for dependencies
1299-
var depJsonStr = depJsonContent.Replace("\r", String.Empty).Replace("\n", String.Empty);
1300-
string depFileContent = CreateDependencyManifestContent(emptyConfigArtifactDigest, 0, depDigest, depFileSize, depFileName, depJsonStr, parentDigest, parentSize);
1301-
File.WriteAllText(depConfigFilePath, depFileContent);
1302-
1303-
var depManifestDigestCreated = CreateDigest(depConfigFilePath, out artifactDigest, out ErrorRecord artifactDigestError);
1304-
if (!depManifestDigestCreated)
1305-
{
1306-
_cmdletPassedIn.ThrowTerminatingError(artifactDigestError);
1307-
}
1308-
1309-
return depManifestDigestCreated;
1310-
}
1311-
1312-
13131124
private string CreateManifestContent(
13141125
string nupkgDigest,
13151126
string configDigest,
@@ -1373,98 +1184,6 @@ private string CreateManifestContent(
13731184
return stringWriter.ToString();
13741185
}
13751186

1376-
private string CreateDependencyManifestContent(
1377-
string depConfigDigest,
1378-
long depConfigFileSize,
1379-
string depDigest,
1380-
long depFileSize,
1381-
string depFileName,
1382-
string dependenciesStr,
1383-
string parentDigest,
1384-
long parentSize)
1385-
{
1386-
StringBuilder stringBuilder = new StringBuilder();
1387-
StringWriter stringWriter = new StringWriter(stringBuilder);
1388-
JsonTextWriter jsonWriter = new JsonTextWriter(stringWriter);
1389-
1390-
jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
1391-
1392-
jsonWriter.WriteStartObject();
1393-
1394-
jsonWriter.WritePropertyName("schemaVersion");
1395-
jsonWriter.WriteValue(2);
1396-
jsonWriter.WritePropertyName("mediaType");
1397-
jsonWriter.WriteValue("application/vnd.oci.image.manifest.v1+json");
1398-
1399-
jsonWriter.WritePropertyName("config");
1400-
jsonWriter.WriteStartObject();
1401-
jsonWriter.WritePropertyName("mediaType");
1402-
jsonWriter.WriteValue("dependency");
1403-
jsonWriter.WritePropertyName("digest");
1404-
jsonWriter.WriteValue($"sha256:{depConfigDigest}");
1405-
jsonWriter.WritePropertyName("size");
1406-
jsonWriter.WriteValue(depConfigFileSize);
1407-
jsonWriter.WriteEndObject();
1408-
1409-
jsonWriter.WritePropertyName("layers");
1410-
jsonWriter.WriteStartArray();
1411-
1412-
jsonWriter.WriteStartObject();
1413-
jsonWriter.WritePropertyName("mediaType");
1414-
jsonWriter.WriteValue("application/vnd.oci.image.layer.v1.tar");
1415-
jsonWriter.WritePropertyName("digest");
1416-
jsonWriter.WriteValue($"sha256:{depDigest}");
1417-
jsonWriter.WritePropertyName("size");
1418-
jsonWriter.WriteValue(depFileSize);
1419-
jsonWriter.WritePropertyName("annotations");
1420-
jsonWriter.WriteStartObject();
1421-
jsonWriter.WritePropertyName("org.opencontainers.image.title");
1422-
jsonWriter.WriteValue(depFileName);;
1423-
jsonWriter.WritePropertyName("dependencies");
1424-
jsonWriter.WriteValue(dependenciesStr);
1425-
jsonWriter.WriteEndObject();
1426-
1427-
jsonWriter.WriteEndObject();
1428-
1429-
jsonWriter.WriteEndArray();
1430-
1431-
1432-
jsonWriter.WritePropertyName("subject");
1433-
jsonWriter.WriteStartObject();
1434-
jsonWriter.WritePropertyName("mediaType");
1435-
jsonWriter.WriteValue("application/vnd.oci.image.manifest.v1+json");
1436-
jsonWriter.WritePropertyName("digest");
1437-
jsonWriter.WriteValue($"sha256:{parentDigest}");
1438-
jsonWriter.WritePropertyName("size");
1439-
jsonWriter.WriteValue(parentSize);
1440-
jsonWriter.WriteEndObject();
1441-
1442-
jsonWriter.WriteEndObject();
1443-
1444-
return stringWriter.ToString();
1445-
}
1446-
1447-
private string CreateDependencyJsonContent(Hashtable dependencies)
1448-
{
1449-
StringBuilder stringBuilder = new StringBuilder();
1450-
StringWriter stringWriter = new StringWriter(stringBuilder);
1451-
JsonTextWriter jsonWriter = new JsonTextWriter(stringWriter);
1452-
1453-
jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented;
1454-
1455-
jsonWriter.WriteStartObject();
1456-
1457-
foreach (string dependencyName in dependencies.Keys)
1458-
{
1459-
jsonWriter.WritePropertyName(dependencyName);
1460-
jsonWriter.WriteValue(dependencies[dependencyName]);
1461-
}
1462-
1463-
jsonWriter.WriteEndObject();
1464-
1465-
return stringWriter.ToString();
1466-
}
1467-
14681187
private bool CreateDigest(string fileName, out string digest, out ErrorRecord error)
14691188
{
14701189
FileInfo fileInfo = new FileInfo(fileName);
@@ -1508,50 +1227,6 @@ private bool CreateDigest(string fileName, out string digest, out ErrorRecord er
15081227
return true;
15091228
}
15101229

1511-
private bool CreateDependencyDigest(out string digest, out ErrorRecord error)
1512-
{
1513-
SHA256 mySHA256 = SHA256.Create();
1514-
string myGuid = new Guid().ToString();
1515-
byte[] byteArray = Encoding.UTF8.GetBytes(myGuid);
1516-
1517-
using (MemoryStream memStream = new MemoryStream(byteArray))
1518-
{
1519-
digest = string.Empty;
1520-
1521-
try
1522-
{
1523-
// Create a MemoryStream for the Guid.
1524-
// Be sure it's positioned to the beginning of the stream.
1525-
memStream.Position = 0;
1526-
// Compute the hash of the MemoryStream.
1527-
byte[] hashValue = mySHA256.ComputeHash(memStream);
1528-
StringBuilder stringBuilder = new StringBuilder();
1529-
foreach (byte b in hashValue)
1530-
stringBuilder.AppendFormat("{0:x2}", b);
1531-
digest = stringBuilder.ToString();
1532-
// Write the name and hash value of the file to the console.
1533-
_cmdletPassedIn.WriteVerbose($"dependency digest: {digest}");
1534-
error = null;
1535-
}
1536-
catch (IOException ex)
1537-
{
1538-
var IOError = new ErrorRecord(ex, $"IOException for .nupkg file: {ex.Message}", ErrorCategory.InvalidOperation, null);
1539-
error = IOError;
1540-
}
1541-
catch (UnauthorizedAccessException ex)
1542-
{
1543-
var AuthorizationError = new ErrorRecord(ex, $"UnauthorizedAccessException for .nupkg file: {ex.Message}", ErrorCategory.PermissionDenied, null);
1544-
error = AuthorizationError;
1545-
}
1546-
}
1547-
if (error != null)
1548-
{
1549-
return false;
1550-
}
1551-
1552-
return true;
1553-
}
1554-
15551230
private string CreateMetadataContent(string manifestFilePath, ResourceType resourceType, Hashtable parsedMetadata, out ErrorRecord metadataCreationError)
15561231
{
15571232
metadataCreationError = null;

0 commit comments

Comments
 (0)