Skip to content

Commit

Permalink
Migrate Import Image to use Azure.ResourceManager.ContainerRegistry (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
mthalman committed Apr 24, 2024
1 parent 41d94b4 commit 736b6b0
Show file tree
Hide file tree
Showing 11 changed files with 56 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using Azure.Core;
using Azure.ResourceManager.ContainerRegistry;
using Microsoft.DotNet.ImageBuilder.Models.Image;
using Microsoft.DotNet.ImageBuilder.ViewModel;

Expand Down Expand Up @@ -39,7 +41,8 @@ public override async Task ExecuteAsync()
{
LoggerService.WriteHeading("COPYING IMAGES");

string resourceId = CopyImageService.GetResourceId(Options.Subscription, Options.ResourceGroup, Manifest.Registry);
ResourceIdentifier resourceId = ContainerRegistryResource.CreateResourceIdentifier(
Options.Subscription, Options.ResourceGroup, CopyImageService.GetBaseRegistryName(Manifest.Registry));

IEnumerable<Task> importTasks = Manifest.FilteredRepos
.Select(repo =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Management.ContainerRegistry.Fluent.Models;
using Azure.ResourceManager.ContainerRegistry.Models;
using Microsoft.DotNet.ImageBuilder.ViewModel;

#nullable enable
Expand Down Expand Up @@ -87,10 +87,13 @@ private Task CopyImageAsync(string fromImage, string destinationRegistryName)
string registry = DockerHelper.GetRegistry(fromImage) ?? DockerHelper.DockerHubRegistry;
string srcImage = DockerHelper.TrimRegistry(fromImage, registry);

ImportSourceCredentials? importSourceCreds = null;
ContainerRegistryImportSourceCredentials? importSourceCreds = null;
if (Options.CredentialsOptions.Credentials.TryGetValue(registry, out RegistryCredentials? registryCreds))
{
importSourceCreds = new ImportSourceCredentials(registryCreds.Password, registryCreds.Username);
importSourceCreds = new ContainerRegistryImportSourceCredentials(registryCreds.Password)
{
Username = registryCreds.Username
};
}

return ImportImageAsync($"{Options.RepoPrefix}{fromImage}", destinationRegistryName, srcImage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// See the LICENSE file in the project root for more information.

using System.Threading.Tasks;
using Microsoft.Azure.Management.ContainerRegistry.Fluent.Models;
using Azure.Core;
using Azure.ResourceManager.ContainerRegistry.Models;

#nullable enable
namespace Microsoft.DotNet.ImageBuilder.Commands
Expand All @@ -23,10 +24,10 @@ public CopyImagesCommand(ICopyImageService copyImageService, ILoggerService logg
public ILoggerService LoggerService { get; }

protected Task ImportImageAsync(string destTagName,
string destRegistryName, string srcTagName, string? srcRegistryName = null, string? srcResourceId = null,
ImportSourceCredentials? sourceCredentials = null) =>
string destRegistryName, string srcTagName, string? srcRegistryName = null, ResourceIdentifier? srcResourceId = null,
ContainerRegistryImportSourceCredentials? sourceCredentials = null) =>
_copyImageService.ImportImageAsync(
Options.Subscription, Options.ResourceGroup, Options.ServicePrincipal, new string[] { destTagName }, destRegistryName,
Options.Subscription, Options.ResourceGroup, Options.ServicePrincipal, [destTagName], destRegistryName,
srcTagName, srcRegistryName, srcResourceId, sourceCredentials, Options.IsDryRun);
}
}
Expand Down
68 changes: 27 additions & 41 deletions src/Microsoft.DotNet.ImageBuilder/src/CopyImageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Azure.Management.ContainerRegistry.Fluent;
using Microsoft.Azure.Management.ContainerRegistry.Fluent.Models;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Azure;
using Azure.Core;
using Azure.Identity;
using Azure.ResourceManager;
using Azure.ResourceManager.ContainerRegistry;
using Azure.ResourceManager.ContainerRegistry.Models;
using Microsoft.DotNet.ImageBuilder.Commands;
using Microsoft.DotNet.ImageBuilder.Services;
using Microsoft.Rest.Azure;
using ImportSource = Microsoft.Azure.Management.ContainerRegistry.Fluent.Models.ImportSource;
using Microsoft.VisualStudio.Services.Common;

namespace Microsoft.DotNet.ImageBuilder;

Expand All @@ -22,53 +21,45 @@ public interface ICopyImageService
{
Task ImportImageAsync(
string subscription, string resourceGroup, ServicePrincipalOptions servicePrincipalOptions, string[] destTagNames,
string destRegistryName, string srcTagName, string? srcRegistryName = null, string? srcResourceId = null,
ImportSourceCredentials? sourceCredentials = null, bool isDryRun = false);
string destRegistryName, string srcTagName, string? srcRegistryName = null, ResourceIdentifier? srcResourceId = null,
ContainerRegistryImportSourceCredentials? sourceCredentials = null, bool isDryRun = false);
}

[Export(typeof(ICopyImageService))]
public class CopyImageService : ICopyImageService
{
private readonly IAzureManagementFactory _azureManagementFactory;
private readonly ILoggerService _loggerService;

[ImportingConstructor]
public CopyImageService(IAzureManagementFactory azureManagementFactory, ILoggerService loggerService)
public CopyImageService(ILoggerService loggerService)
{
_azureManagementFactory = azureManagementFactory;
_loggerService = loggerService;
}

private static string GetBaseRegistryName(string registry) => registry.TrimEnd(".azurecr.io");

public static string GetResourceId(string subscription, string resourceGroup, string registry) =>
$"/subscriptions/{subscription}/resourceGroups/{resourceGroup}/providers" +
$"/Microsoft.ContainerRegistry/registries/{GetBaseRegistryName(registry)}";
public static string GetBaseRegistryName(string registry) => registry.TrimEnd(".azurecr.io");

public async Task ImportImageAsync(
string subscription, string resourceGroup, ServicePrincipalOptions servicePrincipalOptions, string[] destTagNames,
string destRegistryName, string srcTagName, string? srcRegistryName = null, string? srcResourceId = null,
ImportSourceCredentials? sourceCredentials = null, bool isDryRun = false)
string destRegistryName, string srcTagName, string? srcRegistryName = null, ResourceIdentifier? srcResourceId = null,
ContainerRegistryImportSourceCredentials? sourceCredentials = null, bool isDryRun = false)
{
destRegistryName = GetBaseRegistryName(destRegistryName);

AzureCredentials credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(
servicePrincipalOptions.ClientId,
servicePrincipalOptions.Secret,
servicePrincipalOptions.Tenant,
AzureEnvironment.AzureGlobalCloud);
IAzure azure = _azureManagementFactory.CreateAzureManager(credentials, subscription);

ImportImageParametersInner importParams = new()
ClientSecretCredential credentials = new(servicePrincipalOptions.Tenant, servicePrincipalOptions.ClientId, servicePrincipalOptions.Secret);
ArmClient client = new(credentials);
ContainerRegistryResource registryResource = client.GetContainerRegistryResource(
ContainerRegistryResource.CreateResourceIdentifier(subscription, resourceGroup, destRegistryName));
ContainerRegistryImportSource importSrc = new(srcTagName)
{
Mode = "Force",
Source = new ImportSource(
srcTagName,
srcResourceId,
srcRegistryName,
sourceCredentials),
TargetTags = destTagNames
ResourceId = srcResourceId,
RegistryAddress = srcRegistryName,
Credentials = sourceCredentials
};
ContainerRegistryImportImageContent importImageContent = new(importSrc)
{
Mode = ContainerRegistryImportMode.Force
};
importImageContent.TargetTags.AddRange(destTagNames);

string formattedDestTagNames = string.Join(", ", destTagNames.Select(tag => $"'{DockerHelper.GetImageName(destRegistryName, tag)}'").ToArray());
_loggerService.WriteMessage($"Importing {formattedDestTagNames} from '{DockerHelper.GetImageName(srcRegistryName, srcTagName)}'");
Expand All @@ -78,16 +69,11 @@ public async Task ImportImageAsync(
try
{
await RetryHelper.GetWaitAndRetryPolicy<Exception>(_loggerService)
.ExecuteAsync(() => azure.ContainerRegistries.Inner.ImportImageAsync(resourceGroup, destRegistryName, importParams));
.ExecuteAsync(() => registryResource.ImportImageAsync(WaitUntil.Completed, importImageContent));
}
catch (Exception e)
{
string errorMsg = $"Importing Failure: {formattedDestTagNames}";
if (e is CloudException cloudException)
{
errorMsg += Environment.NewLine + cloudException.Body.Message;
}

errorMsg += Environment.NewLine + e.ToString();

_loggerService.WriteMessage(errorMsg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

<ItemGroup>
<!-- Upgrade explicitly referenced package version referenced by Microsoft.Data.SqlClient.5.1.1 and Microsoft.Azure.Kusto.Ingest.11.3.4 -->
<PackageReference Include="Azure.Identity" Version="1.10.3" />
<PackageReference Include="Azure.Identity" Version="1.11.2" />
<PackageReference Include="Azure.ResourceManager.ContainerRegistry" Version="1.2.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.18.0" />
<PackageReference Include="Azure.Storage.Queues" Version="12.16.0" />
<PackageReference Include="Cottle" Version="2.0.10" />
Expand All @@ -21,6 +22,7 @@
<PackageReference Include="Microsoft.DotNet.Git.IssueManager" Version="9.0.0-beta.24123.3" />
<PackageReference Include="Microsoft.DotNet.VersionTools" Version="9.0.0-beta.24151.5" />
<PackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="5.3.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.60.3" />
<PackageReference Include="Microsoft.TeamFoundationServer.Client" Version="16.205.1" />
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
<PackageReference Include="Polly" Version="8.0.0" />
Expand Down

This file was deleted.

This file was deleted.

3 changes: 2 additions & 1 deletion src/Microsoft.DotNet.ImageBuilder/tests/BuildCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Azure.ResourceManager.ContainerRegistry.Models;
using Microsoft.DotNet.ImageBuilder.Commands;
using Microsoft.DotNet.ImageBuilder.Models.Image;
using Microsoft.DotNet.ImageBuilder.Models.Manifest;
Expand Down Expand Up @@ -3627,7 +3628,7 @@ private static void VerifyImportImage(Mock<ICopyImageService> copyImageServiceMo
srcTagName,
srcRegistryName,
null,
It.IsAny<ImportSourceCredentials>(),
It.IsAny<ContainerRegistryImportSourceCredentials>(),
false));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Management.ContainerRegistry.Fluent;
using Microsoft.Azure.Management.ContainerRegistry.Fluent.Models;
using Microsoft.Azure.Management.Fluent;
using Azure.ResourceManager.ContainerRegistry;
using Microsoft.DotNet.ImageBuilder.Commands;
using Microsoft.DotNet.ImageBuilder.Models.Image;
using Microsoft.DotNet.ImageBuilder.Models.Manifest;
using Microsoft.DotNet.ImageBuilder.Services;
using Microsoft.DotNet.ImageBuilder.Tests.Helpers;
using Moq;
using Newtonsoft.Json;
Expand All @@ -35,11 +31,6 @@ public async Task CopyAcrImagesCommand_CustomDockerfileName()

using (TempFolderContext tempFolderContext = TestHelper.UseTempFolder())
{
Mock<IRegistriesOperations> registriesOperationsMock = AzureHelper.CreateRegistriesOperationsMock();
IAzure azure = AzureHelper.CreateAzureMock(registriesOperationsMock);
Mock<IAzureManagementFactory> azureManagementFactoryMock =
AzureHelper.CreateAzureManagementFactoryMock(subscriptionId, azure);

Mock<ICopyImageService> copyImageServiceMock = new();

CopyAcrImagesCommand command = new(copyImageServiceMock.Object, Mock.Of<ILoggerService>());
Expand Down Expand Up @@ -114,7 +105,7 @@ public async Task CopyAcrImagesCommand_CustomDockerfileName()
manifest.Registry,
expectedTag,
null,
CopyImageService.GetResourceId(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
ContainerRegistryResource.CreateResourceIdentifier(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
null,
false));
}
Expand All @@ -133,11 +124,6 @@ public async Task CopyAcrImagesCommand_SharedDockerfile()

using (TempFolderContext tempFolderContext = TestHelper.UseTempFolder())
{
Mock<IRegistriesOperations> registriesOperationsMock = AzureHelper.CreateRegistriesOperationsMock();
IAzure azure = AzureHelper.CreateAzureMock(registriesOperationsMock);
Mock<IAzureManagementFactory> azureManagementFactoryMock =
AzureHelper.CreateAzureManagementFactoryMock(subscriptionId, azure);

Mock<IEnvironmentService> environmentServiceMock = new();
Mock<ICopyImageService> copyImageServiceMock = new();

Expand Down Expand Up @@ -225,7 +211,7 @@ public async Task CopyAcrImagesCommand_SharedDockerfile()
manifest.Registry,
expectedTag,
null,
CopyImageService.GetResourceId(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
ContainerRegistryResource.CreateResourceIdentifier(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
null,
false));
}
Expand All @@ -243,10 +229,6 @@ public async Task CopyAcrImagesCommand_RuntimeDepsSharing()
const string subscriptionId = "my subscription";

using TempFolderContext tempFolderContext = TestHelper.UseTempFolder();
Mock<IRegistriesOperations> registriesOperationsMock = AzureHelper.CreateRegistriesOperationsMock();
IAzure azure = AzureHelper.CreateAzureMock(registriesOperationsMock);
Mock<IAzureManagementFactory> azureManagementFactoryMock =
AzureHelper.CreateAzureManagementFactoryMock(subscriptionId, azure);

Mock<IEnvironmentService> environmentServiceMock = new();
Mock<ICopyImageService> copyImageServiceMock = new();
Expand Down Expand Up @@ -347,7 +329,7 @@ public async Task CopyAcrImagesCommand_RuntimeDepsSharing()
manifest.Registry,
expectedTag,
null,
CopyImageService.GetResourceId(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
ContainerRegistryResource.CreateResourceIdentifier(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
null,
false));
}
Expand All @@ -364,10 +346,6 @@ public async Task SyndicatedTags()
const string subscriptionId = "my subscription";

using TempFolderContext tempFolderContext = TestHelper.UseTempFolder();
Mock<IRegistriesOperations> registriesOperationsMock = AzureHelper.CreateRegistriesOperationsMock();
IAzure azure = AzureHelper.CreateAzureMock(registriesOperationsMock);
Mock<IAzureManagementFactory> azureManagementFactoryMock =
AzureHelper.CreateAzureManagementFactoryMock(subscriptionId, azure);

Mock<IEnvironmentService> environmentServiceMock = new();
Mock<ICopyImageService> copyImageServiceMock = new();
Expand Down Expand Up @@ -469,7 +447,7 @@ public async Task SyndicatedTags()
manifest.Registry,
It.IsAny<string>(),
null,
CopyImageService.GetResourceId(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
ContainerRegistryResource.CreateResourceIdentifier(subscriptionId, command.Options.ResourceGroup, manifest.Registry),
null,
false));
}
Expand Down
Loading

0 comments on commit 736b6b0

Please sign in to comment.