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 samples and test for the national clouds support in ACR #22127

Merged
merged 2 commits into from
Jun 10, 2021
Merged
Changes from 1 commit
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
66 changes: 44 additions & 22 deletions sdk/containerregistry/azure-containers-containerregistry/README.md
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ The [Azure Identity library][identity] provides easy Azure Active Directory supp
Note all the below samples assume you have an endpoint, which is the URL including the name of the login server and the `https://` prefix.
More information at [Azure Container Registry portal][container_registry_create_portal]

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L29-L33 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L31-L35 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
@@ -46,7 +46,7 @@ ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.buildClient();
```

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L37-L41 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L39-L43 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryAsyncClient client = new ContainerRegistryClientBuilder()
@@ -57,20 +57,43 @@ ContainerRegistryAsyncClient client = new ContainerRegistryClientBuilder()

For more information on using AAD with Azure Container Registry, please see the service's [Authentication Overview](https://docs.microsoft.com/azure/container-registry/container-registry-authentication).

#### National Clouds
To authenticate with a registry in a [National Cloud](https://docs.microsoft.com/azure/active-directory/develop/authentication-national-cloud), you will need to make the following additions to your client configuration:
- Set the authorityHost in the credential builder.
- Set the authenticationScope in ContainerRegistryClientBuilder.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L183-L196 -->
```Java
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE_US_GOVERNMENT);
TokenCredential credentials = new DefaultAzureCredentialBuilder()
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();

ContainerRegistryClient containerRegistryClient = new ContainerRegistryClientBuilder()
.endpoint(getEndpoint())
.credential(credentials)
.authenticationScope(getAuthenticationScope())
.buildClient();

containerRegistryClient
.listRepositoryNames()
.forEach(name -> System.out.println(name));
```

### Anonymous access support
If the builder is instantiated without any credentials, the SDK creates the service client for the anonymous pull mode.
The user must use this setting on a registry that has been enabled for anonymous pull.
In this mode, the user can only call listRepositoryNames method and its overload. All the other calls will fail.
For more information please read [Anonymous Pull Access](https://docs.microsoft.com/azure/container-registry/container-registry-faq#how-do-i-enable-anonymous-pull-access)

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L71-L73 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L73-L75 -->
```Java
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.buildClient();
```

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L77-L79 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L79-L81 -->
```Java
ContainerRegistryAsyncClient client = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
@@ -105,7 +128,7 @@ For more information please see [Container Registry Concepts](https://docs.micro

Iterate through the collection of repositories in the registry.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L45-L51 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L47-L53 -->
```Java
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRegistryClient client = new ContainerRegistryClientBuilder()
@@ -118,7 +141,7 @@ client.listRepositoryNames().forEach(repository -> System.out.println(repository

### List tags with anonymous access

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L138-L149 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L140-L151 -->
```Java
ContainerRegistryClient anonymousClient = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
@@ -136,7 +159,7 @@ for (ArtifactTagProperties tag : tags) {

### Set artifact properties

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L117-L130 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L119-L132 -->
```Java
TokenCredential defaultCredential = new DefaultAzureCredentialBuilder().build();

@@ -156,7 +179,7 @@ image.updateTagProperties(

### Delete Images

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L83-L111 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L85-L113 -->
```Java
TokenCredential defaultCredential = new DefaultAzureCredentialBuilder().build();

@@ -190,7 +213,7 @@ for (String repositoryName : client.listRepositoryNames()) {
```

### Delete repository with anonymous access throws
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L153-L165 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L155-L167 -->
```Java
final String endpoint = getEndpoint();
final String repositoryName = getRepositoryName();
@@ -212,20 +235,19 @@ try {
All container registry service operations will throw a
[HttpResponseException][HttpResponseException] on failure.

<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L56-L67 -->
<!-- embedme ./src/samples/java/com/azure/containers/containerregistry/ReadmeSamples.java#L59-L69 -->
```Java
public void getPropertiesThrows() {
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRepository containerRepository = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient()
.getRepository(repositoryName);
try {
containerRepository.getProperties();
} catch (HttpResponseException exception) {
// Do something with the exception.
}
DefaultAzureCredential credential = new DefaultAzureCredentialBuilder().build();
ContainerRepository containerRepository = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient()
.getRepository(repositoryName);
try {
containerRepository.getProperties();
} catch (HttpResponseException exception) {
// Do something with the exception.
}
```

## Next steps
Original file line number Diff line number Diff line change
@@ -107,14 +107,10 @@ public ContainerRegistryClientBuilder endpoint(String endpoint) {
/**
* Sets the authentication scope to be used for getting AAD credentials.
*
* <p>NOTE - This is a temporary property that is added into the system until the service
* exposes this directly via the challenge based auth scheme.
* If this property is not provided then by default Azure public scope is used for authentication.
*</p>
*
* <p>
* Example:- For Azure public cloud this value is same as AzureEnvironment.Azure.managementEndpoint().
* For more information - https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/services-support-managed-identities#azure-resource-manager
* <p> To connect to a different cloud, set this value to "&lt;resource-id&gt;/.default",
* where &lt;resource-id&gt; is one of the Resource IDs listed at
* https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/services-support-managed-identities#azure-resource-manager.
* For example, to connect to the Azure Germany cloud, {@code authenticationScope} is set to "https://management.microsoftazure.de/.default".
* </p>
*
* @param authenticationScope ARM management scope associated with the given registry.
Original file line number Diff line number Diff line change
@@ -132,27 +132,37 @@ static <T, R> PagedResponse<T> getPagedResponseWithContinuationToken(PagedRespon
* @return The exception returned by the public methods.
*/
static Throwable mapException(Throwable exception) {
if (!(exception instanceof AcrErrorsException)) {
return exception;
AcrErrorsException acrException = null;

if (exception instanceof AcrErrorsException) {
acrException = ((AcrErrorsException) exception);
} else if (exception instanceof RuntimeException) {
RuntimeException runtimeException = (RuntimeException) exception;
Throwable throwable = runtimeException.getCause();
if (throwable instanceof AcrErrorsException) {
acrException = (AcrErrorsException) throwable;
}
}

final AcrErrorsException errorsException = ((AcrErrorsException) exception);
final HttpResponse errorHttpResponse = errorsException.getResponse();
if (acrException == null) {
return exception;
}

final HttpResponse errorHttpResponse = acrException.getResponse();
final int statusCode = errorHttpResponse.getStatusCode();
final String errorDetail = errorsException.getMessage();
final String errorDetail = acrException.getMessage();

switch (statusCode) {
case 401:
return new ClientAuthenticationException(errorDetail, errorsException.getResponse(), exception);
return new ClientAuthenticationException(errorDetail, acrException.getResponse(), exception);
case 404:
return new ResourceNotFoundException(errorDetail, errorsException.getResponse(), exception);
return new ResourceNotFoundException(errorDetail, acrException.getResponse(), exception);
case 409:
return new ResourceExistsException(errorDetail, errorsException.getResponse(), exception);
return new ResourceExistsException(errorDetail, acrException.getResponse(), exception);
case 412:
return new ResourceModifiedException(errorDetail, errorsException.getResponse(), exception);
return new ResourceModifiedException(errorDetail, acrException.getResponse(), exception);
default:
return new HttpResponseException(errorDetail, errorsException.getResponse(), exception);
return new HttpResponseException(errorDetail, acrException.getResponse(), exception);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.containers.containerregistry;

import com.azure.core.credential.TokenCredential;
import com.azure.core.management.AzureEnvironment;
import com.azure.core.management.profile.AzureProfile;
import com.azure.identity.DefaultAzureCredentialBuilder;

public class NationalCloudSample {

public static void main(String[] args) {
final String endpoint = "endpoint";
final String authenticationScope = "scope";
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE_US_GOVERNMENT);
TokenCredential credentials = new DefaultAzureCredentialBuilder()
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();

ContainerRegistryAsyncClient asyncClient = new ContainerRegistryClientBuilder()
.endpoint(endpoint)
.credential(credentials)
.authenticationScope(authenticationScope)
.buildAsyncClient();

asyncClient.listRepositoryNames().subscribe(name -> System.out.println(name));
}
}
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@
import com.azure.core.exception.ClientAuthenticationException;
import com.azure.core.exception.HttpResponseException;
import com.azure.core.http.rest.PagedIterable;
import com.azure.core.management.AzureEnvironment;
import com.azure.core.management.profile.AzureProfile;
import com.azure.core.util.Context;
import com.azure.identity.DefaultAzureCredential;
import com.azure.identity.DefaultAzureCredentialBuilder;
@@ -177,5 +179,25 @@ private static String getTagName() {
return null;
}

public void authenticationScopeSample() {
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE_US_GOVERNMENT);
TokenCredential credentials = new DefaultAzureCredentialBuilder()
.authorityHost(profile.getEnvironment().getActiveDirectoryEndpoint())
.build();

ContainerRegistryClient containerRegistryClient = new ContainerRegistryClientBuilder()
.endpoint(getEndpoint())
.credential(credentials)
.authenticationScope(getAuthenticationScope())
.buildClient();

containerRegistryClient
.listRepositoryNames()
.forEach(name -> System.out.println(name));
}

private static String getAuthenticationScope() {
return null;
Copy link
Member

Choose a reason for hiding this comment

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

Let's use a real authentication scope here so the sample is truly runnable

}
}

Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

package com.azure.containers.containerregistry;

import com.azure.core.exception.ClientAuthenticationException;
import com.azure.core.http.HttpClient;
import com.azure.core.test.TestMode;
import com.azure.core.test.implementation.ImplUtils;
@@ -19,6 +20,8 @@
import java.util.stream.Collectors;

import static com.azure.containers.containerregistry.TestUtils.ALPINE_REPOSITORY_NAME;
import static com.azure.containers.containerregistry.TestUtils.AZURE_GLOBAL_AUTHENTICATION_SCOPE;
import static com.azure.containers.containerregistry.TestUtils.AZURE_GOV_AUTHENTICATION_SCOPE;
import static com.azure.containers.containerregistry.TestUtils.DISPLAY_NAME_WITH_ARGUMENTS;
import static com.azure.containers.containerregistry.TestUtils.HELLO_WORLD_REPOSITORY_NAME;
import static com.azure.containers.containerregistry.TestUtils.LATEST_TAG_NAME;
@@ -168,5 +171,24 @@ public void convenienceProperties(HttpClient httpClient) {
assertEquals(registryEndpoint, registryAsyncClient.getEndpoint());
assertEquals(registryEndpoint, registryClient.getEndpoint());
}

@ParameterizedTest(name = DISPLAY_NAME_WITH_ARGUMENTS)
@MethodSource("getHttpClients")
public void authenticationScopeTest(HttpClient httpClient) {
ContainerRegistryClient registryClient = getContainerRegistryBuilder(httpClient)
.authenticationScope(AZURE_GLOBAL_AUTHENTICATION_SCOPE)
.buildClient();

List<String> repositories = registryClient.listRepositoryNames().stream().collect(Collectors.toList());
validateRepositories(repositories);

if (getTestMode() != TestMode.PLAYBACK) {
// Now doing the same should fail with the separate registryClient;
ContainerRegistryClient throwableRegistryClient = getContainerRegistryBuilder(httpClient)
.authenticationScope(AZURE_GOV_AUTHENTICATION_SCOPE)
.buildClient();
assertThrows(ClientAuthenticationException.class, () -> throwableRegistryClient.listRepositoryNames().stream().collect(Collectors.toList()));
}
}
}

Original file line number Diff line number Diff line change
@@ -59,6 +59,8 @@ public class TestUtils {
public static final String QUARANTINE_STATE;
public static final String QUARANTINE_DETAILS;
public static final int HTTP_STATUS_CODE_202;
public static final String AZURE_GLOBAL_AUTHENTICATION_SCOPE;
public static final String AZURE_GOV_AUTHENTICATION_SCOPE;

static {
CONFIGURATION = Configuration.getGlobalConfiguration().clone();
@@ -95,6 +97,8 @@ public class TestUtils {
QUARANTINE_STATE = "quarantine_state";
QUARANTINE_DETAILS = "quaratine_details";
HTTP_STATUS_CODE_202 = 202;
AZURE_GLOBAL_AUTHENTICATION_SCOPE = "https://management.azure.com/.default";
AZURE_GOV_AUTHENTICATION_SCOPE = "https://management.usgovcloudapi.net/.default";
}

static class FakeCredentials implements TokenCredential {
Loading