Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ACR delete tag methods #19680

Merged
8 commits merged into from
Mar 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@
<PackageReference Update="Microsoft.Azure.Graph.RBAC" Version="2.2.2-preview" />
<PackageReference Update="Microsoft.Azure.KeyVault.Core" Version="3.0.3" />
<PackageReference Update="Microsoft.Azure.Management.ContainerRegistry" Version="2.0.0" />
<PackageReference Update="Microsoft.Azure.Management.ContainerRegistry.Fluent" Version="1.37.1" />
<PackageReference Update="Microsoft.Azure.Management.EventGrid" Version="4.0.1-preview" />
<PackageReference Update="Microsoft.Azure.Management.EventHub" Version="2.5.0" />
<PackageReference Update="Microsoft.Azure.Management.HDInsight" Version="4.1.0-preview" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ protected ContainerRepositoryClient() { }
public ContainerRepositoryClient(System.Uri endpoint, string repository, string username, string password) { }
public ContainerRepositoryClient(System.Uri endpoint, string repository, string username, string password, Azure.Containers.ContainerRegistry.ContainerRegistryClientOptions options) { }
public virtual System.Uri Endpoint { get { throw null; } }
public virtual Azure.Response DeleteTag(string tag, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response> DeleteTagAsync(string tag, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.Containers.ContainerRegistry.RepositoryProperties> GetProperties(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual System.Threading.Tasks.Task<Azure.Response<Azure.Containers.ContainerRegistry.RepositoryProperties>> GetPropertiesAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
public virtual Azure.Response<Azure.Containers.ContainerRegistry.TagProperties> GetTagProperties(string tag, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Specialized;
using System.Threading;
using System.Web;

Copy link
Member

@JoshLove-msft JoshLove-msft Mar 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm does the "azp" command not work for container registry, "/azp run net - containerregistry - ci"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you need a slash '/azp run net - core - ci'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

using Azure.Core;
using Azure.Core.Pipeline;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,41 @@ public virtual Response SetTagProperties(string tag, ContentProperties value, Ca
throw;
}
}

/// <summary> Delete tag. </summary>
/// <param name="tag"> Tag name. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
public virtual async Task<Response> DeleteTagAsync(string tag, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(ContainerRepositoryClient)}.{nameof(DeleteTag)}");
scope.Start();
try
{
return await _restClient.DeleteTagAsync(_repository, tag, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
scope.Failed(e);
throw;
}
}

/// <summary> Delete tag. </summary>
/// <param name="tag"> Tag name. </param>
/// <param name="cancellationToken"> The cancellation token to use. </param>
public virtual Response DeleteTag(string tag, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(ContainerRepositoryClient)}.{nameof(DeleteTag)}");
scope.Start();
try
{
return _restClient.DeleteTag(_repository, tag, cancellationToken);
}
catch (Exception e)
{
scope.Failed(e);
throw;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
<ItemGroup>
<PackageReference Include="NUnit" />
<PackageReference Include="NUnit3TestAdapter" />
<PackageReference Include="Microsoft.Azure.Management.ContainerRegistry" VersionOverride="4.0.0" />
<PackageReference Include="Microsoft.Azure.Management.ContainerRegistry.Fluent" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ public class ContainerRegistryTestEnvironment : TestEnvironment
public string Endpoint => GetRecordedVariable("CONTAINERREGISTRY_ENDPOINT");
public string UserName => GetRecordedVariable("CONTAINERREGISTRY_USERNAME", options => options.IsSecret());
public string Password => GetRecordedVariable("CONTAINERREGISTRY_PASSWORD", options => options.IsSecret());
public string Registry => GetRecordedVariable("CONTAINERREGISTRY_REGISTRY_NAME");

public bool IsTestModeLive => this.Mode != RecordedTestMode.Playback;
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,147 +2,185 @@
// Licensed under the MIT License.

using System;
using System.Threading.Tasks;
using System.Collections.Generic;

using Azure.Core.TestFramework;

using Microsoft.Azure.Management.ContainerRegistry;
using Microsoft.Azure.Management.ContainerRegistry.Models;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using NUnit.Framework;
using Task = System.Threading.Tasks.Task;
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved

namespace Azure.Containers.ContainerRegistry.Tests
{
public class ContainerRepositoryClientLiveTests : RecordedTestBase<ContainerRegistryTestEnvironment>
{
private readonly string _repositoryName = "library/hello-world";
private readonly string _tagName = "latest";
private ContainerRepositoryClient _client;

public ContainerRepositoryClientLiveTests(bool isAsync) : base(isAsync)
{
}

[SetUp]
protected async Task CreateClient()
protected ContainerRepositoryClient CreateClient()
{
_client = InstrumentClient(new ContainerRepositoryClient(
return InstrumentClient(new ContainerRepositoryClient(
new Uri(TestEnvironment.Endpoint),
_repositoryName,
TestEnvironment.UserName,
TestEnvironment.Password,
InstrumentClientOptions(new ContainerRegistryClientOptions())
));

await InitializeRepositoryProperties();
await InitializeTagProperties();
}

[OneTimeTearDown]
public async Task TearDown()
{
await InitializeRepositoryProperties();
await InitializeTagProperties();
}

public async Task InitializeRepositoryProperties()
public async Task ImportImage(string tag)
{
await _client.SetPropertiesAsync(
new ContentProperties()
if (TestEnvironment.IsTestModeLive)
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
{
var credential = new AzureCredentials(
new ServicePrincipalLoginInformation
{
ClientId = TestEnvironment.ClientId,
ClientSecret = TestEnvironment.ClientSecret,
},
TestEnvironment.TenantId,
AzureEnvironment.AzureGlobalCloud);

var _registryClient = new ContainerRegistryManagementClient(credential.WithDefaultSubscription(TestEnvironment.SubscriptionId));
_registryClient.SubscriptionId = TestEnvironment.SubscriptionId;

var importSource = new ImportSource
{
CanList = true,
CanRead = true,
CanWrite = true,
CanDelete = true
});

RepositoryProperties properties = await _client.GetPropertiesAsync();

Assert.IsTrue(properties.WriteableProperties.CanList);
Assert.IsTrue(properties.WriteableProperties.CanRead);
Assert.IsTrue(properties.WriteableProperties.CanWrite);
Assert.IsTrue(properties.WriteableProperties.CanDelete);
}

public async Task InitializeTagProperties()
{
await _client.SetTagPropertiesAsync(
_tagName,
new ContentProperties()
{
CanList = true,
CanRead = true,
CanWrite = true,
CanDelete = true
});

RepositoryProperties properties = await _client.GetPropertiesAsync();

Assert.IsTrue(properties.WriteableProperties.CanList);
Assert.IsTrue(properties.WriteableProperties.CanRead);
Assert.IsTrue(properties.WriteableProperties.CanWrite);
Assert.IsTrue(properties.WriteableProperties.CanDelete);
SourceImage = "library/hello-world",
RegistryUri = "registry.hub.docker.com"
};

await _registryClient.Registries.ImportImageAsync(
resourceGroupName: TestEnvironment.ResourceGroup,
registryName: TestEnvironment.Registry,
parameters:
new ImportImageParameters
{
Mode = ImportMode.Force,
Source = importSource,
TargetTags = new List<string>()
{
$"library/hello-world:{tag}"
}
});
}
}

[RecordedTest]
public async Task CanGetRepositoryProperties()
{
RepositoryProperties properties = await _client.GetPropertiesAsync();
// Arrange
ContainerRepositoryClient client = CreateClient();
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved

// Act
RepositoryProperties properties = await client.GetPropertiesAsync();

// Assert
Assert.AreEqual(_repositoryName, properties.Name);
Assert.AreEqual(new Uri(TestEnvironment.Endpoint).Host, properties.Registry);
}

[RecordedTest, NonParallelizable]
public async Task CanSetRepositoryProperties([Values(true, false)] bool canList,
[Values(true, false)] bool canRead,
[Values(true, false)] bool canWrite,
[Values(true, false)] bool canDelete)
public async Task CanSetRepositoryProperties()
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
{
await _client.SetPropertiesAsync(
// Arrange
ContainerRepositoryClient client = CreateClient();
RepositoryProperties repositoryProperties = await client.GetPropertiesAsync();
ContentProperties originalContentProperties = repositoryProperties.WriteableProperties;

// Act
await client.SetPropertiesAsync(
new ContentProperties()
{
CanList = canList,
CanRead = canRead,
CanWrite = canWrite,
CanDelete = canDelete
CanList = false,
CanRead = false,
CanWrite = false,
CanDelete = false,
});

RepositoryProperties properties = await _client.GetPropertiesAsync();
// Assert
RepositoryProperties properties = await client.GetPropertiesAsync();

Assert.AreEqual(canList, properties.WriteableProperties.CanList);
Assert.AreEqual(canRead, properties.WriteableProperties.CanRead);
Assert.AreEqual(canWrite, properties.WriteableProperties.CanWrite);
Assert.AreEqual(canDelete, properties.WriteableProperties.CanDelete);
Assert.IsFalse(properties.WriteableProperties.CanList);
Assert.IsFalse(properties.WriteableProperties.CanRead);
Assert.IsFalse(properties.WriteableProperties.CanWrite);
Assert.IsFalse(properties.WriteableProperties.CanDelete);

// Cleanup
await client.SetPropertiesAsync(originalContentProperties);
}

[RecordedTest]
public async Task CanGetTagProperties()
{
TagProperties properties = await _client.GetTagPropertiesAsync(_tagName);
// Arrange
ContainerRepositoryClient client = CreateClient();
string tag = "latest";

// Act
TagProperties properties = await client.GetTagPropertiesAsync(tag);

Assert.AreEqual(_tagName, properties.Name);
// Assert
Assert.AreEqual(tag, properties.Name);
Assert.AreEqual(_repositoryName, properties.Repository);
Assert.AreEqual(new Uri(TestEnvironment.Endpoint).Host, properties.Registry);
}

[RecordedTest, NonParallelizable]
public async Task CanSetTagProperties([Values(true, false)] bool canList,
[Values(true, false)] bool canRead,
[Values(true, false)] bool canWrite,
[Values(true, false)] bool canDelete)
public async Task CanSetTagProperties()
{
await _client.SetTagPropertiesAsync(
_tagName,
// Arrange
ContainerRepositoryClient client = CreateClient();
string tag = "latest";
TagProperties tagProperties = await client.GetTagPropertiesAsync(tag);
ContentProperties originalContentProperties = tagProperties.ModifiableProperties;

// Act
await client.SetTagPropertiesAsync(
tag,
new ContentProperties()
{
CanList = canList,
CanRead = canRead,
CanWrite = canWrite,
CanDelete = canDelete
CanList = false,
CanRead = false,
CanWrite = false,
CanDelete = false
});

TagProperties properties = await _client.GetTagPropertiesAsync(_tagName);
// Assert
TagProperties properties = await client.GetTagPropertiesAsync(tag);

Assert.IsFalse(properties.ModifiableProperties.CanList);
Assert.IsFalse(properties.ModifiableProperties.CanRead);
Assert.IsFalse(properties.ModifiableProperties.CanWrite);
Assert.IsFalse(properties.ModifiableProperties.CanDelete);

// Cleanup
await client.SetTagPropertiesAsync(tag, originalContentProperties);
}

[RecordedTest, NonParallelizable]
public async Task CanDeleteTag()
{
// Arrange
ContainerRepositoryClient client = CreateClient();
string tag = "test-delete";
await ImportImage(tag);

// Act
await client.DeleteTagAsync(tag);

// Assert

// The delete takes some time, so if we call GetTagProperties() without a delay, we get a 200 response.
annelo-msft marked this conversation as resolved.
Show resolved Hide resolved
await Task.Delay(5000);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Search and Key Vault, we have our own DelayAsync that effectively goes something like this:

protected async ValueTask DelayAsync(TimeSpan? duration = null)
{
  duration ??= TimeSpan.FromSeconds(5);
  if (TestMode == RecordingTestMode.Playback)
  {
    return ValueTask.Completed;
  }
  else
  {
    await Task.Delay(duration);
  }
}

This way, you don't waste time during playback.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool, Heath, thanks for the pointer!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If so, take a look at Search or Key Vault's. It's a bit more robust (involved) than this sample, but this shows the gist of it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...effectively, you can optionally set a delay, or for retries (something else maybe to add, but separate) a polling interval with default. Both should probably take a CancellationToken, not that we use them much (but occasionally).

annelo-msft marked this conversation as resolved.
Show resolved Hide resolved

Assert.AreEqual(canList, properties.ModifiableProperties.CanList);
Assert.AreEqual(canRead, properties.ModifiableProperties.CanRead);
Assert.AreEqual(canWrite, properties.ModifiableProperties.CanWrite);
Assert.AreEqual(canDelete, properties.ModifiableProperties.CanDelete);
Assert.ThrowsAsync<RequestFailedException>(async () => { await client.GetTagPropertiesAsync(tag); });
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading