From 6321e90f61353d504bf02911c2b7c6b48f90aa26 Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 30 Aug 2016 16:53:25 -0700 Subject: [PATCH 01/18] Virtual machine extensions --- .../management/compute/ComputeRoles.java | 46 ++ .../management/compute/VirtualMachine.java | 59 +- .../compute/VirtualMachineExtension.java | 532 ++++++++++++++++++ .../compute/VirtualMachineExtensionImage.java | 72 +++ .../VirtualMachineExtensionImageType.java | 36 ++ .../VirtualMachineExtensionImageTypes.java | 9 + .../VirtualMachineExtensionImageVersion.java | 41 ++ .../VirtualMachineExtensionImageVersions.java | 9 + .../VirtualMachineExtensionImages.java | 40 ++ .../compute/VirtualMachinePublisher.java | 5 + .../implementation/ComputeManager.java | 19 +- .../ExternalChildResourceImpl.java | 54 ++ .../VirtualMachineExtensionImageImpl.java | 76 +++ .../VirtualMachineExtensionImageTypeImpl.java | 49 ++ ...VirtualMachineExtensionImageTypesImpl.java | 35 ++ ...rtualMachineExtensionImageVersionImpl.java | 57 ++ ...tualMachineExtensionImageVersionsImpl.java | 37 ++ .../VirtualMachineExtensionImagesImpl.java | 73 +++ .../VirtualMachineExtensionImpl.java | 202 +++++++ .../VirtualMachineExtensionsImpl.java | 175 ++++++ .../VirtualMachineImagesImpl.java | 6 +- .../implementation/VirtualMachineImpl.java | 32 +- .../VirtualMachinePublisherImpl.java | 15 +- .../VirtualMachinePublishersImpl.java | 17 +- .../implementation/VirtualMachinesImpl.java | 31 +- ...lMachineExtensionImageOperationsTests.java | 115 ++++ ...irtualMachineExtensionOperationsTests.java | 51 ++ .../VirtualMachineImageOperationsTests.java | 5 +- 28 files changed, 1860 insertions(+), 38 deletions(-) create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ComputeRoles.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImage.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageType.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageTypes.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersion.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersions.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypeImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypesImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionsImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImagesImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageOperationsTests.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ComputeRoles.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ComputeRoles.java new file mode 100644 index 000000000000..166bdd1026a0 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ComputeRoles.java @@ -0,0 +1,46 @@ +package com.microsoft.azure.management.compute; + + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Defines values for ComputeRoles. + */ +public enum ComputeRoles { + /** Enum value PaaS. */ + PAAS("PaaS"), + + /** Enum value IaaS. */ + IAAS("IaaS"); + + /** The actual serialized value for a ComputeRoles instance. */ + private String value; + + ComputeRoles(String value) { + this.value = value; + } + + /** + * Parses a serialized value to a ComputeRoles instance. + * + * @param value the serialized value to parse. + * @return the parsed ComputeRoles object, or null if unable to parse. + */ + @JsonCreator + public static ComputeRoles fromString(String value) { + ComputeRoles[] items = ComputeRoles.values(); + for (ComputeRoles item : items) { + if (item.toString().equalsIgnoreCase(value)) { + return item; + } + } + return null; + } + + @JsonValue + @Override + public String toString() { + return this.value; + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java index bb0ebec7e2c2..6f6820b23414 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java @@ -3,7 +3,6 @@ import com.microsoft.azure.CloudException; import com.microsoft.azure.PagedList; import com.microsoft.azure.management.compute.implementation.VirtualMachineInner; -import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionInner; import com.microsoft.azure.management.network.Network; import com.microsoft.azure.management.network.NetworkInterface; import com.microsoft.azure.management.network.PublicIpAddress; @@ -19,6 +18,7 @@ import java.io.IOException; import java.util.List; +import java.util.Map; /** * An immutable client-side representation of an Azure virtual machine. @@ -192,9 +192,10 @@ public interface VirtualMachine extends String licenseType(); /** - * @return the resources value + * @return the extensions attached to the Azure Virtual Machine + * @throws Exception exceptions thrown from the cloud or from serialization/deserialization. */ - List resources(); + Map extensions() throws Exception; /** * @return the plan value @@ -812,6 +813,19 @@ interface WithSecondaryNetworkInterface { WithCreate withExistingSecondaryNetworkInterface(NetworkInterface networkInterface); } + /** + * The stage of the virtual machine definition allowing to specify extensions. + */ + interface WithExtension { + /** + * Specifies definition of an extension to be attached to the virtual machine. + * + * @param name the reference name for the extension + * @return the stage representing configuration for the extension + */ + VirtualMachineExtension.DefinitionStages.Blank defineNewExtension(String name); + } + /** * The stage of the definition which contains all the minimum required inputs for * the resource to be created (via {@link WithCreate#create()}), but also allows @@ -826,7 +840,8 @@ interface WithCreate extends DefinitionStages.WithStorageAccount, DefinitionStages.WithDataDisk, DefinitionStages.WithAvailabilitySet, - DefinitionStages.WithSecondaryNetworkInterface { + DefinitionStages.WithSecondaryNetworkInterface, + DefinitionStages.WithExtension { } } @@ -936,6 +951,37 @@ interface WithSecondaryNetworkInterface { */ Update withoutSecondaryNetworkInterface(String name); } + + /** + * The stage of the virtual machine definition allowing to specify extensions. + */ + interface WithExtension { + /** + * Specifies definition of an extension to be attached to the virtual machine. + * + * @param name the reference name for the extension + * @return the stage representing configuration for the extension + */ + VirtualMachineExtension + .UpdateDefinitionStages + .Blank defineNewExtension(String name); + + /** + * Begins the description of an update of an existing extension of this virtual machine. + * + * @param name the reference name for the extension + * @return the stage representing updatable VM definition + */ + VirtualMachineExtension.Update updateExtension(String name); + + /** + * Detaches an extension with the given name from the virtual machine. + * + * @param name the reference name for the extension to be removed/uninstalled + * @return the stage representing updatable VM definition + */ + Update withoutExtension(String name); + } } /** @@ -948,7 +994,8 @@ interface Update extends Appliable, Resource.UpdateWithTags, UpdateStages.WithDataDisk, - UpdateStages.WithSecondaryNetworkInterface { + UpdateStages.WithSecondaryNetworkInterface, + UpdateStages.WithExtension { /** * Specifies the caching type for the Operating System disk. * @@ -981,4 +1028,4 @@ interface Update extends */ Update withSize(VirtualMachineSizeTypes size); } -} +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java new file mode 100644 index 000000000000..454bd134fc16 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java @@ -0,0 +1,532 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionInner; +import com.microsoft.azure.management.resources.fluentcore.arm.models.ChildResource; +import com.microsoft.azure.management.resources.fluentcore.arm.models.Resource; +import com.microsoft.azure.management.resources.fluentcore.model.Attachable; +import com.microsoft.azure.management.resources.fluentcore.model.Refreshable; +import com.microsoft.azure.management.resources.fluentcore.model.Settable; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; + +import java.util.HashMap; +import java.util.Map; + +/** + * An immutable client-side representation of an Azure virtual machine extension. + * An extension associated with a virtual machine will be created from a {@link VirtualMachineExtensionImage }. + */ +public interface VirtualMachineExtension extends + Wrapper, + Refreshable, + ChildResource, + Resource { + /** + * @return the publisher name of the virtual machine extension image this extension is created from + */ + String publisherName(); + + /** + * @return the type name of the virtual machine extension image this extension is created from + */ + String typeName(); + + /** + * @return the version name of the virtual machine extension image this extension is created from + */ + String versionName(); + + /** + * @return true if this extension is configured to upgrade automatically when a new minor version of + * virtual machine extension image that this extension based on is published + */ + boolean autoUpgradeMinorVersionEnabled(); + + /** + * @return the public settings of the virtual machine extension as key value pairs + */ + Map publicSettings(); + + /** + * @return the public settings of the virtual machine extension as a json string + */ + String publicSettingsAsJsonString(); + + /** + * @return the instance view of this virtual machine extension + */ + VirtualMachineExtensionInstanceView instanceView(); + + /** + * Grouping of virtual machine extension definition stages as a part of parent virtual machine definition. + */ + interface DefinitionStages { + /** + * The first stage of a virtual machine extension definition. + * + * @param the return type of the final {@link WithAttach#attach()} + */ + interface Blank + extends WithImageOrPublisher { + } + + /** + * The stage of the virtual machine extension definition allowing to specify extension image or specify name of + * the virtual machine extension publisher. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithImageOrPublisher + extends WithPublisher { + /** + * Specifies the virtual machine extension image to use. + * + * @param image the image + * @return the next stage of the definition + */ + WithAttach withImage(VirtualMachineExtensionImage image); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the publisher of the + * virtual machine extension image this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithPublisher { + /** + * Specifies the name of the virtual machine extension image publisher. + * + * @param extensionImagePublisherName the publisher name + * @return the next stage of the definition + */ + WithType withPublisher(String extensionImagePublisherName); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the type of the virtual machine + * extension image this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithType { + /** + * Specifies the type of the virtual machine extension image. + * + * @param extensionImageTypeName the image type name + * @return the next stage of the definition + */ + WithVersion withType(String extensionImageTypeName); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the type of the virtual machine + * extension version this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithVersion { + /** + * Specifies the version of the virtual machine image extension. + * + * @param extensionImageVersionName the version name + * @return the next stage of the definition + */ + WithAttach withVersion(String extensionImageVersionName); + } + + /** The final stage of the virtual machine extension definition. + *

+ * At this stage, any remaining optional settings can be specified, or the virtual machine extension definition + * can be attached to the parent virtual machine definition using {@link VirtualMachineExtension.DefinitionStages.WithAttach#attach()}. + * @param the return type of {@link VirtualMachineExtension.DefinitionStages.WithAttach#attach()} + */ + interface WithAttach extends + Attachable.InDefinition, + WithAutoUpgradeMinorVersion, + WithSettings, + WithTags { + } + + /** + * The stage of the virtual machine extension definition allowing to enable or disable auto upgrade of the + * extension when when a new minor version of virtual machine extension image gets published. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithAutoUpgradeMinorVersion { + /** + * enables auto upgrade of the extension. + * + * @return the next stage of the definition + */ + WithAttach withAutoUpgradeMinorVersionEnabled(); + + /** + * disables auto upgrade of the extension. + * + * @return the next stage of the definition + */ + WithAttach withAutoUpgradeMinorVersionDisabled(); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the public and private settings. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithSettings { + /** + * Specifies a public settings entry. + * + * @param key the key of a public settings entry + * @param value the value of the public settings entry + * @return the next stage of the definition + */ + WithAttach withPublicSetting(String key, Object value); + + /** + * Specifies a private settings entry. + * + * @param key the key of a private settings entry + * @param value the value of the private settings entry + * @return the next stage of the definition + */ + WithAttach withProtectedSetting(String key, Object value); + + /** + * Specifies public settings. + * + * @param settings the public settings + * @return the next stage of the definition + */ + WithAttach withPublicSettings(HashMap settings); + + /** + * Specifies private settings. + * + * @param settings the private settings + * @return the next stage of the definition + */ + WithAttach withProtectedSettings(HashMap settings); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the tags. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithTags { + /** + * Specifies tags for the virtual machine extension as a {@link Map}. + * @param tags a {@link Map} of tags + * @return the next stage of the definition + */ + WithAttach withTags(Map tags); + + /** + * Adds a tag to the virtual machine extension. + * @param key the key for the tag + * @param value the value for the tag + * @return the next stage of the definition + */ + WithAttach withTag(String key, String value); + } + } + + /** + * The entirety of a virtual machine extension definition as a part of parent definition. + * + * @param the return type of the final {@link Attachable#attach()} + */ + interface Definition extends + DefinitionStages.Blank, + DefinitionStages.WithImageOrPublisher, + DefinitionStages.WithPublisher, + DefinitionStages.WithType, + DefinitionStages.WithVersion, + DefinitionStages.WithAttach { + } + + /** + * Grouping of virtual machine extension definition stages as part of parent virtual machine update. + */ + interface UpdateDefinitionStages { + /** + * The first stage of a virtual machine extension definition. + * + * @param the return type of the final {@link WithAttach#attach()} + */ + interface Blank + extends WithImageOrPublisher { + } + + /** + * The stage of the virtual machine extension allowing to specify extension image or specify name of the + * virtual machine extension publisher. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithImageOrPublisher + extends WithPublisher { + /** + * Specifies the virtual machine extension image to use. + * + * @param image the image + * @return the next stage of the definition + */ + WithAttach withImage(VirtualMachineExtensionImage image); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the publisher of the + * virtual machine extension image this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithPublisher { + /** + * Specifies the name of the virtual machine extension image publisher. + * + * @param extensionImagePublisherName the publisher name + * @return the next stage of the definition + */ + WithType withPublisher(String extensionImagePublisherName); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the type of the virtual machine + * extension image this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithType { + /** + * Specifies the type of the virtual machine extension image. + * + * @param extensionImageTypeName the image type name + * @return the next stage of the definition + */ + WithVersion withType(String extensionImageTypeName); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the type of the virtual machine + * extension version this extension is based on. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithVersion { + /** + * Specifies the version of the virtual machine image extension. + * + * @param extensionImageVersionName the version name + * @return the next stage of the definition + */ + WithAttach withVersion(String extensionImageVersionName); + } + + /** The final stage of the virtual machine extension definition. + *

+ * At this stage, any remaining optional settings can be specified, or the virtual machine extension definition + * can be attached to the parent virtual machine definition using {@link VirtualMachineExtension.UpdateDefinitionStages.WithAttach#attach()}. + * @param the return type of {@link VirtualMachineExtension.UpdateDefinitionStages.WithAttach#attach()} + */ + interface WithAttach extends + Attachable.InUpdate, + WithAutoUpgradeMinorVersion, + WithSettings, + WithTags { + } + + /** + * The stage of the virtual machine extension definition allowing to enable or disable auto upgrade of the + * extension when when a new minor version of virtual machine extension image gets published. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithAutoUpgradeMinorVersion { + /** + * enables auto upgrade of the extension. + * + * @return the next stage of the definition + */ + WithAttach withAutoUpgradeMinorVersionEnabled(); + + /** + * disables auto upgrade of the extension. + * + * @return the next stage of the definition + */ + WithAttach withAutoUpgradeMinorVersionDisabled(); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the public and private settings. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithSettings { + /** + * Specifies a public settings entry. + * + * @param key the key of a public settings entry + * @param value the value of the public settings entry + * @return the next stage of the definition + */ + WithAttach withPublicSetting(String key, Object value); + + /** + * Specifies a private settings entry. + * + * @param key the key of a private settings entry + * @param value the value of the private settings entry + * @return the next stage of the definition + */ + WithAttach withProtectedSetting(String key, Object value); + + /** + * Specifies public settings. + * + * @param settings the public settings + * @return the next stage of the definition + */ + WithAttach withPublicSettings(HashMap settings); + + /** + * Specifies private settings. + * + * @param settings the private settings + * @return the next stage of the definition + */ + WithAttach withProtectedSettings(HashMap settings); + } + + /** + * The stage of the virtual machine extension definition allowing to specify the tags. + * + * @param the return type of {@link WithAttach#attach()} + */ + interface WithTags { + /** + * Specifies tags for the resource as a {@link Map}. + * @param tags a {@link Map} of tags + * @return the next stage of the resource definition + */ + WithAttach withTags(Map tags); + + /** + * Adds a tag to the resource. + * @param key the key for the tag + * @param value the value for the tag + * @return the next stage of the resource definition + */ + WithAttach withTag(String key, String value); + } + } + + /** + * The entirety of a virtual machine extension definition as a part of parent update. + * @param the return type of the final {@link Attachable#attach()} + */ + interface UpdateDefinition extends + UpdateDefinitionStages.Blank, + UpdateDefinitionStages.WithImageOrPublisher, + UpdateDefinitionStages.WithPublisher, + UpdateDefinitionStages.WithType, + UpdateDefinitionStages.WithVersion, + UpdateDefinitionStages.WithAttach { + } + + /** + * Grouping of virtual machine extension update stages. + */ + interface UpdateStages { + /** + * The stage of the virtual machine extension update allowing to enable or disable auto upgrade of the + * extension when when a new minor version of virtual machine extension image gets published. + */ + interface WithAutoUpgradeMinorVersion { + /** + * enables auto upgrade of the extension. + * + * @return the next stage of the update + */ + Update withAutoUpgradeMinorVersionEnabled(); + + /** + * enables auto upgrade of the extension. + * + * @return the next stage of the update + */ + Update withAutoUpgradeMinorVersionDisabled(); + } + + /** + * The stage of the virtual machine extension update allowing to add or update public and private settings. + */ + interface WithSettings { + /** + * Specifies a public settings entry. + * + * @param key the key of a public settings entry + * @param value the value of the public settings entry + * @return the next stage of the update + */ + Update withPublicSetting(String key, Object value); + + /** + * Specifies public settings. + * + * @param settings the public settings + * @return the next stage of the update + */ + Update withPublicSettings(HashMap settings); + + /** + * Specifies private settings. + * + * @param settings the private settings + * @return the next stage of the update + */ + Update withProtectedSettings(HashMap settings); + } + + /** + * The stage of the virtual machine extension update allowing to add or update tags. + */ + interface WithTags { + /** + * Specifies tags for the virtual machine extension as a {@link Map}. + * @param tags a {@link Map} of tags + * @return the next stage of the update + */ + Update withTags(Map tags); + + /** + * Adds a tag to the virtual machine extension. + * @param key the key for the tag + * @param value the value for the tag + * @return the next stage of the update + */ + Update withTag(String key, String value); + + /** + * Removes a tag from the virtual machine extension. + * @param key the key of the tag to remove + * @return the next stage of the update + */ + Update withoutTag(String key); + } + } + + /** + * The entirety of virtual machine extension update as a part of parent virtual machine update. + */ + interface Update extends + Settable, + UpdateStages.WithAutoUpgradeMinorVersion, + UpdateStages.WithSettings, + UpdateStages.WithTags { + + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImage.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImage.java new file mode 100644 index 000000000000..6cf667ebaaff --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImage.java @@ -0,0 +1,72 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionImageInner; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; + +/** + * An immutable client-side representation of an Azure virtual machine extension image. + *

+ * Note: Azure virtual machine extension image is also referred as virtual machine extension handler. + */ +public interface VirtualMachineExtensionImage extends + Wrapper { + /** + * @return the resource ID of the extension image + */ + String id(); + + /** + * @return the region in which virtual machine extension image is available + */ + String regionName(); + + /** + * @return the name of the publisher of the virtual machine extension image + */ + String publisherName(); + + /** + * @return the name of the virtual machine extension image type this image belongs to + */ + String typeName(); + + /** + * @return the name of the virtual machine extension image version this image represents + */ + String versionName(); + + /** + * @return the operating system this virtual machine extension image supports + */ + OperatingSystemTypes osType(); + + /** + * @return the type of role this virtual machine extension image supports + */ + ComputeRoles computeRole(); + + /** + * @return the schema defined by publisher, where extension consumers should provide settings in a matching schema + *

+ * Note this field will be null since server provide null for them + */ + String handlerSchema(); + + /** + * @return true if the extension can be used on xRP Virtual Machine ScaleSets. + *

+ * Note by default existing extensions are usable on scale sets, but there might be cases where a publisher wants to + * explicitly indicate the extension is only enabled for Compute Resource Provider VMs but not Virtual Machine ScaleSets. + */ + boolean vmScaleSetEnabled(); + + /** + * @return true if the handler can support multiple extensions. + */ + boolean supportsMultipleExtensions(); + + /** + * @return the virtual machine extension image version this image belongs to + */ + VirtualMachineExtensionImageVersion version(); +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageType.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageType.java new file mode 100644 index 000000000000..6b6544d94e45 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageType.java @@ -0,0 +1,36 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionImageInner; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; + +/** + * An immutable client-side representation of an Azure virtual machine extension image type. + */ +public interface VirtualMachineExtensionImageType extends + Wrapper { + /** + * @return the resource ID of the virtual machine extension image type + */ + String id(); + + /** + * @return the name of the virtual machine extension image type + */ + String name(); + + /** + * @return the region in which virtual machine extension image type is available + */ + String regionName(); + + /** + * @return the publisher of this virtual machine extension image type + */ + VirtualMachinePublisher publisher(); + + /** + * @return Virtual machine image extension versions available in this type + */ + VirtualMachineExtensionImageVersions versions(); +} + diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageTypes.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageTypes.java new file mode 100644 index 000000000000..00b9f10d46af --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageTypes.java @@ -0,0 +1,9 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListing; + +/** + * Entry point to virtual machine image extension types. + */ +public interface VirtualMachineExtensionImageTypes extends SupportsListing { +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersion.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersion.java new file mode 100644 index 000000000000..07f4deba96b7 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersion.java @@ -0,0 +1,41 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionImageInner; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; + +import java.io.IOException; + +/** + * An immutable client-side representation of an Azure virtual machine extension image version. + */ +public interface VirtualMachineExtensionImageVersion extends + Wrapper { + /** + * @return the resource ID of the extension image version + */ + String id(); + + /** + * @return the name of the virtual machine extension image version + */ + String name(); + + /** + * @return the region in which virtual machine extension image version is available + */ + String regionName(); + + /** + * @return the virtual machine extension image type this version belongs to + */ + VirtualMachineExtensionImageType type(); + + /** + * @return virtual machine extension image this version represents + * + * @throws CloudException thrown for an invalid response from the service + * @throws IOException exception thrown from serialization/deserialization + */ + VirtualMachineExtensionImage image() throws CloudException, IOException; +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersions.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersions.java new file mode 100644 index 000000000000..ba4c300fcc5d --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageVersions.java @@ -0,0 +1,9 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListing; + +/** + * Entry point to virtual machine image extension versions. + */ +public interface VirtualMachineExtensionImageVersions extends SupportsListing { +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java new file mode 100644 index 000000000000..9b62c9626960 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java @@ -0,0 +1,40 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.PagedList; +import com.microsoft.azure.management.resources.fluentcore.arm.Region; +import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListingByRegion; + +import java.io.IOException; + +/** + * Entry point to virtual machine extension image management API. + */ +public interface VirtualMachineExtensionImages extends SupportsListingByRegion { + /** + * @return entry point to virtual machine extension image publishers + */ + VirtualMachinePublishers publishers(); + + /** + * Lists all the virtual machine extension images available in a given region. + *

+ * Note this is a very long running call, as it enumerates through all publishers, types and versions. + * @return list of virtual machine extension images + * @param regionName the name of the region as used internally by Azure + * @throws CloudException exceptions thrown from the cloud + * @throws IOException exceptions thrown from serialization/deserialization + */ + PagedList listByRegion(String regionName) throws CloudException, IOException; + + /** + * Lists all the virtual machine extension images available in a given region. + *

+ * Note this is a very long running call, as it enumerates through all publishers, types and versions. + * @return list of virtual machine extension images + * @param region the region to list the images from + * @throws CloudException exceptions thrown from the cloud + * @throws IOException exceptions thrown from serialization/deserialization + */ + PagedList listByRegion(Region region) throws CloudException, IOException; +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachinePublisher.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachinePublisher.java index 99addcb673d8..f9d84263e672 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachinePublisher.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachinePublisher.java @@ -25,4 +25,9 @@ public interface VirtualMachinePublisher { * @return the offers from this publisher */ VirtualMachineOffers offers(); + + /** + * @return the virtual machine image extensions from this publisher. + */ + VirtualMachineExtensionImageTypes extensionTypes(); } \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ComputeManager.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ComputeManager.java index 312aa8c9d05e..50744531ae9a 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ComputeManager.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ComputeManager.java @@ -2,6 +2,7 @@ import com.microsoft.azure.AzureEnvironment; import com.microsoft.azure.management.compute.AvailabilitySets; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImages; import com.microsoft.azure.management.compute.VirtualMachineImages; import com.microsoft.azure.management.compute.VirtualMachines; import com.microsoft.azure.management.network.implementation.NetworkManager; @@ -23,6 +24,7 @@ public final class ComputeManager extends Manager the fluent model type of the child resource + * @param Azure inner resource class type + * @param the implementation type of the fluent model type + * @param the parent Azure resource class type + */ +public abstract class ExternalChildResourceImpl< + FluentModelT extends Resource & ChildResource, + InnerModelT extends com.microsoft.azure.Resource, + FluentModelImplT extends ExternalChildResourceImpl, + ParentT extends Resource> + extends + ResourceImpl { + protected ParentT parent; + private State state = State.None; + + protected ExternalChildResourceImpl(String name, ParentT parent, InnerModelT inner) { + super(name, inner); + this.parent = parent; + } + + /** + * @return the state of this child resource + */ + public State state() { + return this.state; + } + + /** + * Update the state. + * + * @param newState the new state of this child resource + */ + public void setState(State newState) { + this.state = newState; + } + + /** + * The possible state of an child resource in-memory. + */ + enum State { + None, + ToBeCreated, + ToBeUpdated, + ToBeRemoved + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageImpl.java new file mode 100644 index 000000000000..d08165aec8f3 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageImpl.java @@ -0,0 +1,76 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.management.compute.ComputeRoles; +import com.microsoft.azure.management.compute.OperatingSystemTypes; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImage; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersion; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.WrapperImpl; + +/** + * The implementation for {@link VirtualMachineExtensionImage}. + */ +public class VirtualMachineExtensionImageImpl + extends WrapperImpl + implements VirtualMachineExtensionImage { + private final VirtualMachineExtensionImageVersion version; + + VirtualMachineExtensionImageImpl(VirtualMachineExtensionImageVersion version, VirtualMachineExtensionImageInner inner) { + super(inner); + this.version = version; + } + + @Override + public String id() { + return this.inner().id(); + } + + @Override + public String regionName() { + return this.inner().location(); + } + + @Override + public String publisherName() { + return this.version().type().publisher().name(); + } + + @Override + public String typeName() { + return this.version().type().name(); + } + + @Override + public String versionName() { + return this.version().name(); + } + + @Override + public OperatingSystemTypes osType() { + return OperatingSystemTypes.fromString(this.inner().operatingSystem()); + } + + @Override + public ComputeRoles computeRole() { + return ComputeRoles.fromString(this.inner().computeRole()); + } + + @Override + public String handlerSchema() { + return this.inner().handlerSchema(); + } + + @Override + public boolean vmScaleSetEnabled() { + return this.inner().vmScaleSetEnabled(); + } + + @Override + public boolean supportsMultipleExtensions() { + return this.inner().supportsMultipleExtensions(); + } + + @Override + public VirtualMachineExtensionImageVersion version() { + return this.version; + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypeImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypeImpl.java new file mode 100644 index 000000000000..ad9f5bd43ff2 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypeImpl.java @@ -0,0 +1,49 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersions; +import com.microsoft.azure.management.compute.VirtualMachinePublisher; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.WrapperImpl; + +/** + * The implementation for {@link VirtualMachineExtensionImageType}. + */ +class VirtualMachineExtensionImageTypeImpl + extends WrapperImpl + implements VirtualMachineExtensionImageType { + private final VirtualMachineExtensionImagesInner client; + private final VirtualMachinePublisher publisher; + + VirtualMachineExtensionImageTypeImpl(VirtualMachineExtensionImagesInner client, + VirtualMachinePublisher publisher, + VirtualMachineExtensionImageInner inner) { + super(inner); + this.client = client; + this.publisher = publisher; + } + + @Override + public String id() { + return this.inner().id(); + } + + @Override + public String name() { + return this.inner().name(); + } + + @Override + public String regionName() { + return this.inner().location(); + } + + @Override + public VirtualMachinePublisher publisher() { + return this.publisher; + } + + @Override + public VirtualMachineExtensionImageVersions versions() { + return new VirtualMachineExtensionImageVersionsImpl(this.client, this); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypesImpl.java new file mode 100644 index 000000000000..edf1eafaf034 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageTypesImpl.java @@ -0,0 +1,35 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.PagedList; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageTypes; +import com.microsoft.azure.management.compute.VirtualMachinePublisher; +import com.microsoft.azure.management.resources.fluentcore.arm.collection.implementation.ReadableWrappersImpl; + +import java.io.IOException; + +/** + * The implementation for {@link VirtualMachineExtensionImageTypes}. + */ +class VirtualMachineExtensionImageTypesImpl + extends ReadableWrappersImpl + implements VirtualMachineExtensionImageTypes { + private final VirtualMachineExtensionImagesInner client; + private final VirtualMachinePublisher publisher; + + VirtualMachineExtensionImageTypesImpl(VirtualMachineExtensionImagesInner client, VirtualMachinePublisher publisher) { + this.client = client; + this.publisher = publisher; + } + + @Override + public PagedList list() throws CloudException, IOException { + return wrapList(this.client.listTypes(this.publisher.region().toString(), this.publisher.name()).getBody()); + } + + @Override + protected VirtualMachineExtensionImageTypeImpl wrapModel(VirtualMachineExtensionImageInner inner) { + return new VirtualMachineExtensionImageTypeImpl(this.client, this.publisher, inner); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionImpl.java new file mode 100644 index 000000000000..f7299f38e72c --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionImpl.java @@ -0,0 +1,57 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImage; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersion; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.WrapperImpl; +import com.microsoft.rest.ServiceResponse; + +import java.io.IOException; + +/** + * The implementation for {@link VirtualMachineExtensionImageVersion}. + */ +class VirtualMachineExtensionImageVersionImpl + extends WrapperImpl + implements VirtualMachineExtensionImageVersion { + private final VirtualMachineExtensionImagesInner client; + private final VirtualMachineExtensionImageType type; + + VirtualMachineExtensionImageVersionImpl(VirtualMachineExtensionImagesInner client, + VirtualMachineExtensionImageType extensionImageType, + VirtualMachineExtensionImageInner inner) { + super(inner); + this.client = client; + this.type = extensionImageType; + } + + @Override + public String id() { + return this.inner().id(); + } + + @Override + public String name() { + return this.inner().name(); + } + + @Override + public String regionName() { + return this.inner().location(); + } + + @Override + public VirtualMachineExtensionImageType type() { + return this.type; + } + + @Override + public VirtualMachineExtensionImage image() throws CloudException, IOException { + ServiceResponse response = this.client.get(this.regionName(), + this.type().publisher().name(), + this.type().name(), + this.name()); + return new VirtualMachineExtensionImageImpl(this, response.getBody()); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionsImpl.java new file mode 100644 index 000000000000..345ef3c10d7e --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImageVersionsImpl.java @@ -0,0 +1,37 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.PagedList; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersion; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersions; +import com.microsoft.azure.management.resources.fluentcore.arm.collection.implementation.ReadableWrappersImpl; + +import java.io.IOException; + +/** + * The implementation for {@link VirtualMachineExtensionImageVersions}. + */ +public class VirtualMachineExtensionImageVersionsImpl + extends ReadableWrappersImpl + implements VirtualMachineExtensionImageVersions { + private final VirtualMachineExtensionImagesInner client; + private final VirtualMachineExtensionImageType type; + + VirtualMachineExtensionImageVersionsImpl(VirtualMachineExtensionImagesInner client, VirtualMachineExtensionImageType type) { + this.client = client; + this.type = type; + } + + @Override + public PagedList list() throws CloudException, IOException { + return wrapList(this.client.listVersions(this.type.regionName(), + this.type.publisher().name(), + this.type.name()).getBody()); + } + + @Override + protected VirtualMachineExtensionImageVersionImpl wrapModel(VirtualMachineExtensionImageInner inner) { + return new VirtualMachineExtensionImageVersionImpl(this.client, this.type, inner); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImagesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImagesImpl.java new file mode 100644 index 000000000000..a68205c4b21b --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImagesImpl.java @@ -0,0 +1,73 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.CloudException; +import com.microsoft.azure.PagedList; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImage; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersion; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImages; +import com.microsoft.azure.management.compute.VirtualMachinePublisher; +import com.microsoft.azure.management.compute.VirtualMachinePublishers; +import com.microsoft.azure.management.resources.fluentcore.arm.Region; +import com.microsoft.azure.management.resources.fluentcore.utils.PagedListConverter; + +import java.io.IOException; + +/** + * The implementation for {@link VirtualMachineExtensionImages}. + */ +class VirtualMachineExtensionImagesImpl + implements VirtualMachineExtensionImages { + private final VirtualMachinePublishers publishers; + + VirtualMachineExtensionImagesImpl(VirtualMachinePublishers publishers) { + this.publishers = publishers; + } + + @Override + public PagedList listByRegion(Region region) throws CloudException, IOException { + return listByRegion(region.toString()); + } + + @Override + public PagedList listByRegion(String regionName) throws CloudException, IOException { + PagedList publishers = this.publishers().listByRegion(regionName); + + PagedList extensionTypes = + new ChildListFlattener<>(publishers, new ChildListFlattener.ChildListLoader() { + @Override + public PagedList loadList(VirtualMachinePublisher publisher) throws CloudException, IOException { + return publisher.extensionTypes().list(); + } + }).flatten(); + + PagedList extensionTypeVersions = + new ChildListFlattener<>(extensionTypes, new ChildListFlattener.ChildListLoader() { + @Override + public PagedList loadList(VirtualMachineExtensionImageType type) throws CloudException, IOException { + return type.versions().list(); + } + }).flatten(); + + PagedListConverter converter = + new PagedListConverter() { + @Override + public VirtualMachineExtensionImage typeConvert(VirtualMachineExtensionImageVersion virtualMachineExtensionImageVersion) { + try { + return virtualMachineExtensionImageVersion.image(); + } catch (CloudException cloudException) { + throw new RuntimeException(cloudException); + } catch (IOException ioException) { + throw new RuntimeException(ioException); + } + } + }; + + return converter.convert(extensionTypeVersions); + } + + @Override + public VirtualMachinePublishers publishers() { + return this.publishers; + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java new file mode 100644 index 000000000000..81a0ca93c99a --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -0,0 +1,202 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.management.compute.VirtualMachine; +import com.microsoft.azure.management.compute.VirtualMachineExtension; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImage; +import com.microsoft.azure.management.compute.VirtualMachineExtensionInstanceView; +import com.microsoft.rest.ServiceResponse; +import rx.Observable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Implementation of {@link VirtualMachineExtension}. + */ +class VirtualMachineExtensionImpl + extends ExternalChildResourceImpl + implements VirtualMachineExtension, + VirtualMachineExtension.Definition, + VirtualMachineExtension.UpdateDefinition, + VirtualMachineExtension.Update { + private final VirtualMachineExtensionsInner client; + private HashMap publicSettings; + private HashMap protectedSettings; + + VirtualMachineExtensionImpl(String name, + VirtualMachineImpl parent, + VirtualMachineExtensionInner inner, + VirtualMachineExtensionsInner client) { + super(name, parent, inner); + this.client = client; + + if (this.inner().settings() == null) { + this.publicSettings = new LinkedHashMap<>(); + this.inner().withSettings(this.publicSettings); + } else { + this.publicSettings = (LinkedHashMap) this.inner().settings(); + } + + if (this.inner().protectedSettings() == null) { + this.protectedSettings = new LinkedHashMap<>(); + this.inner().withProtectedSettings(this.protectedSettings); + } + } + + protected static VirtualMachineExtensionImpl newVirtualMachineExtension(String name, + VirtualMachineImpl parent, + VirtualMachineExtensionsInner client) { + VirtualMachineExtensionImpl extension = new VirtualMachineExtensionImpl(name, + parent, + new VirtualMachineExtensionInner(), + client); + return extension; + } + + @Override + public String publisherName() { + return this.inner().publisher(); + } + + @Override + public String typeName() { + return this.inner().type(); + } + + @Override + public String versionName() { + return this.inner().typeHandlerVersion(); + } + + @Override + public boolean autoUpgradeMinorVersionEnabled() { + return this.inner().autoUpgradeMinorVersion(); + } + + @Override + public Map publicSettings() { + return Collections.unmodifiableMap(this.publicSettings); + } + + @Override + public String publicSettingsAsJsonString() { + return null; + } + + @Override + public VirtualMachineExtensionInstanceView instanceView() { + return this.inner().instanceView(); + } + + @Override + public VirtualMachineExtension refresh() throws Exception { + ServiceResponse response = + this.client.get(this.parent.resourceGroupName(), this.parent.name(), this.name()); + this.setInner(response.getBody()); + return this; + } + + @Override + public Observable createResourceAsync() { + return this.client.createOrUpdateAsync(this.parent.resourceGroupName(), + this.parent.name(), + this.name(), + this.inner()) + .map(innerToFluentMap(this)); + } + + @Override + public VirtualMachineExtension createResource() throws Exception { + return createResourceAsync().toBlocking().first(); + } + + @Override + public VirtualMachineExtensionImpl withAutoUpgradeMinorVersionEnabled() { + this.inner().withAutoUpgradeMinorVersion(true); + return this; + } + + @Override + public VirtualMachineExtensionImpl withAutoUpgradeMinorVersionDisabled() { + this.inner().withAutoUpgradeMinorVersion(false); + return this; + } + + @Override + public VirtualMachineExtensionImpl withImage(VirtualMachineExtensionImage image) { + this.inner().withPublisher(image.publisherName()) + .withVirtualMachineExtensionType(image.typeName()) + .withTypeHandlerVersion(image.versionName()); + return this; + } + + @Override + public VirtualMachineExtensionImpl withPublisher(String extensionImagePublisherName) { + this.inner().withPublisher(extensionImagePublisherName); + return this; + } + + @Override + public VirtualMachineExtensionImpl withPublicSetting(String key, Object value) { + this.publicSettings.put(key, value); + return this; + } + + @Override + public VirtualMachineExtensionImpl withProtectedSetting(String key, Object value) { + this.protectedSettings.put(key, value); + return this; + } + + @Override + public VirtualMachineExtensionImpl withPublicSettings(HashMap settings) { + this.publicSettings.clear(); + this.publicSettings.putAll(settings); + return this; + } + + @Override + public VirtualMachineExtensionImpl withProtectedSettings(HashMap settings) { + this.protectedSettings.clear(); + this.protectedSettings.putAll(settings); + return this; + } + + @Override + public VirtualMachineExtensionImpl withType(String extensionImageTypeName) { + this.inner().withVirtualMachineExtensionType(extensionImageTypeName); + return this; + } + + @Override + public VirtualMachineExtensionImpl withVersion(String extensionImageVersionName) { + this.inner().withTypeHandlerVersion(extensionImageVersionName); + return this; + } + + @Override + public VirtualMachineImpl parent() { + this.nullifySettingsIfEmpty(); + return this.parent; + } + + @Override + public VirtualMachineImpl attach() { + this.nullifySettingsIfEmpty(); + return this.parent.withExtension(this); + } + + private void nullifySettingsIfEmpty() { + if (this.publicSettings.size() == 0) { + this.inner().withSettings(null); + } + if (this.protectedSettings.size() == 0) { + this.inner().withProtectedSettings(null); + } + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java new file mode 100644 index 000000000000..1086ab7d8bd7 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java @@ -0,0 +1,175 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.management.compute.VirtualMachineExtension; +import com.microsoft.rest.ServiceResponse; +import rx.Observable; +import rx.functions.Action1; +import rx.functions.Func1; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Represents a extension collection associated with a virtual machine. + */ +class VirtualMachineExtensionsImpl { + private final VirtualMachineExtensionsInner client; + private final VirtualMachineImpl parent; + private ConcurrentMap extensions; + private boolean requireRefresh = false; + + VirtualMachineExtensionsImpl(VirtualMachineExtensionsInner client, VirtualMachineImpl parent) { + this.extensions = new ConcurrentHashMap<>(); + this.client = client; + this.parent = parent; + this.initializeExtensionsFromParent(); + } + + Map asMap() { + if (requireRefresh) { + try { + parent.refresh(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + initializeExtensionsFromParent(); + } + Map result = new HashMap<>(); + for (Map.Entry extensionEntry : this.extensions.entrySet()) { + result.put(extensionEntry.getKey(), extensionEntry.getValue()); + } + return Collections.unmodifiableMap(result); + } + + VirtualMachineExtensionImpl define(String name) { + if (findExtension(name) != null) { + throw new IllegalArgumentException("An extension with name '" + name + "' already exists"); + } + VirtualMachineExtensionImpl extension = VirtualMachineExtensionImpl + .newVirtualMachineExtension(name, this.parent, this.client); + extension.setState(VirtualMachineExtensionImpl.State.ToBeCreated); + return extension; + } + + VirtualMachineExtensionImpl update(String name) { + VirtualMachineExtensionImpl extension = findExtension(name); + if (extension == null + || extension.state() == VirtualMachineExtensionImpl.State.ToBeCreated) { + throw new IllegalArgumentException("An extension with name '" + name + "' not found"); + } + if (extension.state() == VirtualMachineExtensionImpl.State.ToBeRemoved) { + throw new IllegalArgumentException("An extension with name '" + name + "' is marked for deletion"); + } + extension.setState(VirtualMachineExtensionImpl.State.ToBeUpdated); + return extension; + } + + void remove(String name) { + VirtualMachineExtensionImpl extension = findExtension(name); + if (extension == null + || extension.state() == VirtualMachineExtensionImpl.State.ToBeCreated) { + throw new IllegalArgumentException("An extension with name '" + name + "' not found"); + } + extension.setState(VirtualMachineExtensionImpl.State.ToBeRemoved); + } + + void addExtension(VirtualMachineExtensionImpl extension) { + this.extensions.put(extension.name(), extension); + } + + Observable commitAsync() { + final VirtualMachineExtensionsImpl self = this; + List items = new ArrayList<>(); + for (VirtualMachineExtensionImpl extension : this.extensions.values()) { + items.add(extension); + } + + Observable deleteStream = Observable.from(items) + .filter(new Func1() { + @Override + public Boolean call(VirtualMachineExtensionImpl extension) { + return extension.state() == ExternalChildResourceImpl.State.ToBeRemoved; + } + }).flatMap(new Func1>() { + @Override + public Observable call(final VirtualMachineExtensionImpl extension) { + + return self.client.deleteAsync(self.parent.resourceGroupName(), + self.parent.name(), + extension.name()) + .map(new Func1, VirtualMachineExtensionImpl>() { + @Override + public VirtualMachineExtensionImpl call(ServiceResponse response) { + self.extensions.remove(extension.name()); + return extension; + } + }); + } + }).doOnError(new Action1() { + @Override + public void call(Throwable throwable) { + self.extensions.clear(); + } + }); + + Observable setStream = Observable.from(items) + .filter(new Func1() { + @Override + public Boolean call(VirtualMachineExtensionImpl extension) { + return extension.state() == ExternalChildResourceImpl.State.ToBeUpdated + || extension.state() == ExternalChildResourceImpl.State.ToBeCreated; + } + }).flatMap(new Func1>() { + @Override + public Observable call(final VirtualMachineExtensionImpl extension) { + return extension.createResourceAsync() + .map(new Func1() { + @Override + public VirtualMachineExtensionImpl call(VirtualMachineExtension e) { + extension.setState(ExternalChildResourceImpl.State.None); + return extension; + } + }); + } + }).doOnError(new Action1() { + @Override + public void call(Throwable throwable) { + self.extensions.clear(); + } + }); + + return deleteStream.mergeWith(setStream) + .filter(new Func1() { + @Override + public Boolean call(VirtualMachineExtensionImpl extension) { + return extension.state() == ExternalChildResourceImpl.State.None; + } + }); + } + + private void initializeExtensionsFromParent() { + if (parent.inner().resources() != null) { + for (VirtualMachineExtensionInner innerExtension : this.parent.inner().resources()) { + this.extensions.put(innerExtension.name(), + new VirtualMachineExtensionImpl(innerExtension.name(), + this.parent, + innerExtension, + this.client)); + } + } + } + + private VirtualMachineExtensionImpl findExtension(String name) { + for (Map.Entry extensionEntry : this.extensions.entrySet()) { + if (extensionEntry.getKey().equalsIgnoreCase(name)) { + return extensionEntry.getValue(); + } + } + return null; + } +} diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImagesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImagesImpl.java index 37f59899c4d0..48ee88179860 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImagesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImagesImpl.java @@ -18,8 +18,8 @@ class VirtualMachineImagesImpl implements VirtualMachineImages { private final VirtualMachinePublishers publishers; - VirtualMachineImagesImpl(VirtualMachineImagesInner client) { - this.publishers = new VirtualMachinePublishersImpl(client); + VirtualMachineImagesImpl(VirtualMachinePublishers publishers) { + this.publishers = publishers; } @Override @@ -63,4 +63,4 @@ public VirtualMachinePublishers publishers() { return this.publishers; } -} +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java index b68e24d0248c..160c4141fb29 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java @@ -29,6 +29,7 @@ import com.microsoft.azure.management.compute.VirtualHardDisk; import com.microsoft.azure.management.compute.VirtualMachine; import com.microsoft.azure.management.compute.VirtualMachineDataDisk; +import com.microsoft.azure.management.compute.VirtualMachineExtension; import com.microsoft.azure.management.compute.VirtualMachineInstanceView; import com.microsoft.azure.management.compute.VirtualMachineSize; import com.microsoft.azure.management.compute.VirtualMachineSizeTypes; @@ -57,6 +58,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -113,10 +115,13 @@ class VirtualMachineImpl private NetworkInterface.DefinitionStages.WithCreate nicDefinitionWithCreate; // Virtual machine size converter private final PagedListConverter virtualMachineSizeConverter; + // The entry point to manage extensions associated with the virtual machine + private VirtualMachineExtensionsImpl virtualMachineExtensions; VirtualMachineImpl(String name, VirtualMachineInner innerModel, VirtualMachinesInner client, + VirtualMachineExtensionsInner extensionsClient, final ComputeManager computeManager, final StorageManager storageManager, final NetworkManager networkManager) { @@ -135,6 +140,7 @@ public VirtualMachineSize typeConvert(VirtualMachineSizeInner inner) { return new VirtualMachineSizeImpl(inner); } }; + this.virtualMachineExtensions = new VirtualMachineExtensionsImpl(extensionsClient, this); initializeDataDisks(); } @@ -637,6 +643,13 @@ public VirtualMachineImpl withExistingSecondaryNetworkInterface(NetworkInterface return this; } + // Virtual machine optional extension settings + + @Override + public VirtualMachineExtensionImpl defineNewExtension(String name) { + return this.virtualMachineExtensions.define(name); + } + // Virtual machine update only settings @Override @@ -694,6 +707,17 @@ public VirtualMachineImpl withoutSecondaryNetworkInterface(String name) { return this; } + @Override + public VirtualMachineExtensionImpl updateExtension(String name) { + return this.virtualMachineExtensions.update(name); + } + + @Override + public VirtualMachineImpl withoutExtension(String name) { + this.virtualMachineExtensions.remove(name); + return this; + } + /************************************************** * . * Getters @@ -813,8 +837,8 @@ public String licenseType() { } @Override - public List resources() { - return inner().resources(); + public Map extensions() throws Exception { + return this.virtualMachineExtensions.asMap(); } @Override @@ -910,6 +934,10 @@ public VirtualMachine call(ServiceResponse virtualMachineIn } // Helpers + VirtualMachineImpl withExtension(VirtualMachineExtensionImpl extension) { + this.virtualMachineExtensions.addExtension(extension); + return this; + } VirtualMachineImpl withDataDisk(DataDiskImpl dataDisk) { this.inner() diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublisherImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublisherImpl.java index 3bf6212231f7..0c8cceada269 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublisherImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublisherImpl.java @@ -5,6 +5,7 @@ */ package com.microsoft.azure.management.compute.implementation; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageTypes; import com.microsoft.azure.management.compute.VirtualMachineOffers; import com.microsoft.azure.management.compute.VirtualMachinePublisher; import com.microsoft.azure.management.resources.fluentcore.arm.Region; @@ -17,11 +18,13 @@ class VirtualMachinePublisherImpl private final Region location; private final String publisher; private final VirtualMachineOffers offers; + private final VirtualMachineExtensionImageTypes types; - VirtualMachinePublisherImpl(Region location, String publisher, VirtualMachineImagesInner client) { + VirtualMachinePublisherImpl(Region location, String publisher, VirtualMachineImagesInner imagesClient, VirtualMachineExtensionImagesInner extensionsClient) { this.location = location; this.publisher = publisher; - this.offers = new VirtualMachineOffersImpl(client, this); + this.offers = new VirtualMachineOffersImpl(imagesClient, this); + this.types = new VirtualMachineExtensionImageTypesImpl(extensionsClient, this); } @Override @@ -38,4 +41,10 @@ public String name() { public VirtualMachineOffers offers() { return this.offers; } -} + + @Override + public VirtualMachineExtensionImageTypes extensionTypes() { + return this.types; + } + +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublishersImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublishersImpl.java index 1c32f8e6d041..fb101c9d6451 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublishersImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinePublishersImpl.java @@ -21,10 +21,12 @@ class VirtualMachinePublishersImpl extends ReadableWrappersImpl implements VirtualMachinePublishers { - private final VirtualMachineImagesInner innerCollection; + private final VirtualMachineImagesInner imagesInnerCollection; + private final VirtualMachineExtensionImagesInner extensionsInnerCollection; - VirtualMachinePublishersImpl(VirtualMachineImagesInner innerCollection) { - this.innerCollection = innerCollection; + VirtualMachinePublishersImpl(VirtualMachineImagesInner imagesInnerCollection, VirtualMachineExtensionImagesInner extensionsInnerCollection) { + this.imagesInnerCollection = imagesInnerCollection; + this.extensionsInnerCollection = extensionsInnerCollection; } @Override @@ -34,11 +36,14 @@ public PagedList listByRegion(Region region) throws Clo @Override protected VirtualMachinePublisherImpl wrapModel(VirtualMachineImageResourceInner inner) { - return new VirtualMachinePublisherImpl(Region.fromName(inner.location()), inner.name(), this.innerCollection); + return new VirtualMachinePublisherImpl(Region.fromName(inner.location()), + inner.name(), + this.imagesInnerCollection, + this.extensionsInnerCollection); } @Override public PagedList listByRegion(String regionName) throws CloudException, IOException { - return wrapList(innerCollection.listPublishers(regionName).getBody()); + return wrapList(imagesInnerCollection.listPublishers(regionName).getBody()); } -} +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinesImpl.java index 6ab462fddc54..52425272010b 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachinesImpl.java @@ -31,22 +31,25 @@ */ class VirtualMachinesImpl extends GroupableResourcesImpl< - VirtualMachine, - VirtualMachineImpl, - VirtualMachineInner, - VirtualMachinesInner, - ComputeManager> + VirtualMachine, + VirtualMachineImpl, + VirtualMachineInner, + VirtualMachinesInner, + ComputeManager> implements VirtualMachines { private final StorageManager storageManager; private final NetworkManager networkManager; private final VirtualMachineSizesImpl vmSizes; + private final VirtualMachineExtensionsInner virtualMachineExtensionsClient; VirtualMachinesImpl(VirtualMachinesInner client, + VirtualMachineExtensionsInner virtualMachineExtensionsClient, VirtualMachineSizesInner virtualMachineSizesClient, ComputeManager computeManager, StorageManager storageManager, NetworkManager networkManager) { super(client, computeManager); + this.virtualMachineExtensionsClient = virtualMachineExtensionsClient; this.storageManager = storageManager; this.networkManager = networkManager; this.vmSizes = new VirtualMachineSizesImpl(virtualMachineSizesClient); @@ -141,18 +144,19 @@ public VirtualMachineSizes sizes() { protected VirtualMachineImpl wrapModel(String name) { VirtualMachineInner inner = new VirtualMachineInner(); inner.withStorageProfile(new StorageProfile() - .withOsDisk(new OSDisk()) - .withDataDisks(new ArrayList())); + .withOsDisk(new OSDisk()) + .withDataDisks(new ArrayList())); inner.withOsProfile(new OSProfile()); inner.withHardwareProfile(new HardwareProfile()); inner.withNetworkProfile(new NetworkProfile() .withNetworkInterfaces(new ArrayList())); return new VirtualMachineImpl(name, - inner, - this.innerCollection, - super.myManager, - this.storageManager, - this.networkManager); + inner, + this.innerCollection, + this.virtualMachineExtensionsClient, + super.myManager, + this.storageManager, + this.networkManager); } @Override @@ -160,8 +164,9 @@ protected VirtualMachineImpl wrapModel(VirtualMachineInner virtualMachineInner) return new VirtualMachineImpl(virtualMachineInner.name(), virtualMachineInner, this.innerCollection, + this.virtualMachineExtensionsClient, super.myManager, this.storageManager, this.networkManager); } -} +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageOperationsTests.java new file mode 100644 index 000000000000..6ab088b32956 --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImageOperationsTests.java @@ -0,0 +1,115 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.resources.fluentcore.arm.Region; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.List; + +public class VirtualMachineExtensionImageOperationsTests extends ComputeManagementTestBase { + @BeforeClass + public static void setup() throws Exception { + createClients(); + } + + @AfterClass + public static void cleanup() throws Exception { + } + + @Test + public void canListExtensionImages() throws Exception { + final int maxListing = 20; + int count = 0; + List extensionImages = + computeManager.virtualMachineExtensionImages() + .listByRegion(Region.US_EAST); + // Lazy listing + for (VirtualMachineExtensionImage extensionImage : extensionImages) { + Assert.assertNotNull(extensionImage); + count++; + if (count >= maxListing) { + break; + } + } + Assert.assertTrue(count == maxListing); + } + + @Test + public void canGetExtensionTypeVersionAndImage() throws Exception { + List extensionImages = + computeManager.virtualMachineExtensionImages() + .listByRegion(Region.US_EAST); + + final String dockerExtensionPublisherName = "Microsoft.Azure.Extensions"; + final String dockerExtensionImageTypeName = "DockerExtension"; + + // Lookup Azure docker extension publisher + // + List publishers = + computeManager.virtualMachineExtensionImages() + .publishers() + .listByRegion(Region.US_EAST); + + VirtualMachinePublisher azureDockerExtensionPublisher = null; + for (VirtualMachinePublisher publisher : publishers) { + if (publisher.name().equalsIgnoreCase(dockerExtensionPublisherName)) { + azureDockerExtensionPublisher = publisher; + break; + } + } + Assert.assertNotNull(azureDockerExtensionPublisher); + + // Lookup Azure docker extension type + // + VirtualMachineExtensionImageTypes extensionImageTypes = azureDockerExtensionPublisher.extensionTypes(); + Assert.assertTrue(extensionImageTypes.list().size() > 0); + + VirtualMachineExtensionImageType dockerExtensionImageType = null; + for (VirtualMachineExtensionImageType extensionImageType : extensionImageTypes.list()) { + if (extensionImageType.name().equalsIgnoreCase(dockerExtensionImageTypeName)) { + dockerExtensionImageType = extensionImageType; + break; + } + } + Assert.assertNotNull(dockerExtensionImageType); + + Assert.assertNotNull(dockerExtensionImageType.id()); + Assert.assertTrue(dockerExtensionImageType.name().equalsIgnoreCase(dockerExtensionImageTypeName)); + Assert.assertTrue(dockerExtensionImageType.regionName().equalsIgnoreCase(Region.US_EAST.toString())); + Assert.assertTrue(dockerExtensionImageType.id() + .toLowerCase() + .endsWith("/Providers/Microsoft.Compute/Locations/eastus/Publishers/Microsoft.Azure.Extensions/ArtifactTypes/VMExtension/Types/DockerExtension".toLowerCase())); + Assert.assertNotNull(dockerExtensionImageType.publisher()); + Assert.assertTrue(dockerExtensionImageType.publisher().name().equalsIgnoreCase(dockerExtensionPublisherName)); + + // Fetch Azure docker extension versions + // + VirtualMachineExtensionImageVersions extensionImageVersions = dockerExtensionImageType.versions(); + Assert.assertTrue(extensionImageVersions.list().size() > 0); + + VirtualMachineExtensionImageVersion extensionImageFirstVersion = null; + for (VirtualMachineExtensionImageVersion extensionImageVersion : extensionImageVersions.list()) { + extensionImageFirstVersion = extensionImageVersion; + break; + } + + Assert.assertNotNull(extensionImageFirstVersion); + String versionName = extensionImageFirstVersion.name(); + Assert.assertTrue(extensionImageFirstVersion.id() + .toLowerCase() + .endsWith(("/Providers/Microsoft.Compute/Locations/eastus/Publishers/Microsoft.Azure.Extensions/ArtifactTypes/VMExtension/Types/DockerExtension/Versions/" + versionName).toLowerCase())); + Assert.assertNotNull(extensionImageFirstVersion.type()); + + // Fetch the Azure docker extension image + // + VirtualMachineExtensionImage dockerExtensionImage = extensionImageFirstVersion.image(); + + Assert.assertTrue(dockerExtensionImage.regionName().equalsIgnoreCase(Region.US_EAST.toString())); + Assert.assertTrue(dockerExtensionImage.publisherName().equalsIgnoreCase(dockerExtensionPublisherName)); + Assert.assertTrue(dockerExtensionImage.typeName().equalsIgnoreCase(dockerExtensionImageTypeName)); + Assert.assertTrue(dockerExtensionImage.versionName().equalsIgnoreCase(versionName)); + Assert.assertTrue(dockerExtensionImage.osType() == OperatingSystemTypes.LINUX || dockerExtensionImage.osType() == OperatingSystemTypes.WINDOWS); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java new file mode 100644 index 000000000000..8092c7673148 --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java @@ -0,0 +1,51 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.resources.fluentcore.utils.ResourceNamer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import java.util.ArrayList; + +public class VirtualMachineExtensionOperationsTests extends ComputeManagementTestBase { + private static final String RG_NAME = ResourceNamer.randomResourceName("jsdkunittest", 15); + private static final String LOCATION = "eastus"; + private static final String VMNAME = "javavm"; + + @BeforeClass + public static void setup() throws Exception { + createClients(); + } + + @AfterClass + public static void cleanup() throws Exception { + } + + @Test + public void canInstallUpdateUninstallExtension() throws Exception { + final String mySqlInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; + final String installCommand = "bash install_mysql_server_5.6.sh Abc.123x("; + // Create VM and add extension + VirtualMachine vm = computeManager.virtualMachines() + .define(VMNAME) + .withRegion(LOCATION) + .withNewResourceGroup(RG_NAME) + .withNewPrimaryNetwork("10.0.0.0/28") + .withPrimaryPrivateIpAddressDynamic() + .withoutPrimaryPublicIpAddress() + .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_14_04_LTS) + .withRootUserName("Foo12") + .withPassword("BaR@12abc!") + .withSize(VirtualMachineSizeTypes.STANDARD_D3) + .defineNewExtension("CustomScriptForLinux") + .withPublisher("Microsoft.OSTCExtensions") + .withType("CustomScriptForLinux") + .withVersion("1.4") + .withAutoUpgradeMinorVersionEnabled() + .withPublicSetting("fileUris", new ArrayList() {{ + add(mySqlInstallScript); + }}) + .withPublicSetting("commandToExecute", installCommand) + .attach() + .create(); + } +} \ No newline at end of file diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineImageOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineImageOperationsTests.java index 1d6d8d758d56..42763a33d7bf 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineImageOperationsTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineImageOperationsTests.java @@ -21,7 +21,7 @@ public static void cleanup() throws Exception { @Test public void canListVirtualMachineImages() throws Exception { - int maxListing = 20; + final int maxListing = 20; int count = 0; PagedList images = computeManager.virtualMachineImages() .listByRegion(Region.US_EAST); @@ -31,7 +31,6 @@ public void canListVirtualMachineImages() throws Exception { if (count >= maxListing) { break; } - System.out.println(count + ":" + image.publisherName() + ":" + image.offer() + ":" + image.version()); } Assert.assertTrue(count == maxListing); @@ -69,4 +68,4 @@ public void canListVirtualMachineImages() throws Exception { Assert.assertNotNull(diskImage.lun()); } } -} +} \ No newline at end of file From d742be80ce3831c7788b0b1c6467b094440c47d3 Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 30 Aug 2016 17:41:36 -0700 Subject: [PATCH 02/18] Simplifying the error handling in stream --- .../VirtualMachineExtensionsImpl.java | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java index 1086ab7d8bd7..3e6f49ef83fd 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java @@ -30,10 +30,14 @@ class VirtualMachineExtensionsImpl { this.initializeExtensionsFromParent(); } + /** + * @return the extensions as a name value map + */ Map asMap() { if (requireRefresh) { try { parent.refresh(); + this.requireRefresh = false; } catch (Exception ex) { throw new RuntimeException(ex); } @@ -46,6 +50,12 @@ Map asMap() { return Collections.unmodifiableMap(result); } + /** + * Starts an extension definition chain. + * + * @param name the reference name of the extension to be added + * @return the extension + */ VirtualMachineExtensionImpl define(String name) { if (findExtension(name) != null) { throw new IllegalArgumentException("An extension with name '" + name + "' already exists"); @@ -56,6 +66,12 @@ VirtualMachineExtensionImpl define(String name) { return extension; } + /** + * Starts an extension update chain. + * + * @param name the reference name of the extension to be updated + * @return the extension + */ VirtualMachineExtensionImpl update(String name) { VirtualMachineExtensionImpl extension = findExtension(name); if (extension == null @@ -69,6 +85,11 @@ VirtualMachineExtensionImpl update(String name) { return extension; } + /** + * Mark the extension with given name as to be removed. + * + * @param name the reference name of the extension to be removed + */ void remove(String name) { VirtualMachineExtensionImpl extension = findExtension(name); if (extension == null @@ -82,6 +103,11 @@ void addExtension(VirtualMachineExtensionImpl extension) { this.extensions.put(extension.name(), extension); } + /** + * Commits the changes in the extension collection. + * + * @return the stream of updated extensions + */ Observable commitAsync() { final VirtualMachineExtensionsImpl self = this; List items = new ArrayList<>(); @@ -110,11 +136,6 @@ public VirtualMachineExtensionImpl call(ServiceResponse response) { } }); } - }).doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - self.extensions.clear(); - } }); Observable setStream = Observable.from(items) @@ -136,11 +157,6 @@ public VirtualMachineExtensionImpl call(VirtualMachineExtension e) { } }); } - }).doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - self.extensions.clear(); - } }); return deleteStream.mergeWith(setStream) @@ -149,9 +165,18 @@ public void call(Throwable throwable) { public Boolean call(VirtualMachineExtensionImpl extension) { return extension.state() == ExternalChildResourceImpl.State.None; } - }); + }).doOnError(new Action1() { + @Override + public void call(Throwable throwable) { + self.extensions.clear(); + self.requireRefresh = true; + } + }); } + /** + * Populate the collection from the parent. + */ private void initializeExtensionsFromParent() { if (parent.inner().resources() != null) { for (VirtualMachineExtensionInner innerExtension : this.parent.inner().resources()) { @@ -164,6 +189,11 @@ private void initializeExtensionsFromParent() { } } + /** + * + * @param name the extension to lookup + * @return the extension + */ private VirtualMachineExtensionImpl findExtension(String name) { for (Map.Entry extensionEntry : this.extensions.entrySet()) { if (extensionEntry.getKey().equalsIgnoreCase(name)) { From 166dbdb361d19ad7875d88c87fe0b2fd139eb1ca Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 30 Aug 2016 21:04:23 -0700 Subject: [PATCH 03/18] applyAsync is now must for CreatableUpdatable types --- .../compute/implementation/VirtualMachineExtensionImpl.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java index 81a0ca93c99a..0c785eb4f6a0 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -101,6 +101,11 @@ public VirtualMachineExtension refresh() throws Exception { return this; } + @Override + public Observable applyAsync() { + return createResourceAsync(); + } + @Override public Observable createResourceAsync() { return this.client.createOrUpdateAsync(this.parent.resourceGroupName(), From 0c047780c60285f81b33f1d01924887dd2abf828 Mon Sep 17 00:00:00 2001 From: anuchan Date: Wed, 31 Aug 2016 16:20:47 -0700 Subject: [PATCH 04/18] Adding ExternalChildResourcesImpl and relaxing bounding constraints on ExternalChildResourceImpl --- .../compute/ExternalChildResource.java | 16 ++ .../compute/VirtualMachineExtension.java | 19 +- .../ExternalChildResourceImpl.java | 60 ++-- .../ExternalChildResourcesImpl.java | 265 ++++++++++++++++++ .../VirtualMachineExtensionImpl.java | 95 +++++-- .../VirtualMachineExtensionsImpl.java | 198 ++++--------- .../implementation/VirtualMachineImpl.java | 1 + 7 files changed, 463 insertions(+), 191 deletions(-) create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ExternalChildResource.java create mode 100644 azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ExternalChildResource.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ExternalChildResource.java new file mode 100644 index 000000000000..79cff6570e79 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/ExternalChildResource.java @@ -0,0 +1,16 @@ +package com.microsoft.azure.management.compute; + +import com.microsoft.azure.management.resources.fluentcore.arm.models.ChildResource; +import com.microsoft.azure.management.resources.fluentcore.model.Refreshable; + +/** + * Represents an external child resource. + * + * @param fluent type of the external child resource + */ +public interface ExternalChildResource extends ChildResource, Refreshable { + /** + * @return the id of the external child resource + */ + String id(); +} diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java index 454bd134fc16..ba7af954b132 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java @@ -1,10 +1,7 @@ package com.microsoft.azure.management.compute; import com.microsoft.azure.management.compute.implementation.VirtualMachineExtensionInner; -import com.microsoft.azure.management.resources.fluentcore.arm.models.ChildResource; -import com.microsoft.azure.management.resources.fluentcore.arm.models.Resource; import com.microsoft.azure.management.resources.fluentcore.model.Attachable; -import com.microsoft.azure.management.resources.fluentcore.model.Refreshable; import com.microsoft.azure.management.resources.fluentcore.model.Settable; import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; @@ -16,10 +13,8 @@ * An extension associated with a virtual machine will be created from a {@link VirtualMachineExtensionImage }. */ public interface VirtualMachineExtension extends - Wrapper, - Refreshable, - ChildResource, - Resource { + ExternalChildResource, + Wrapper { /** * @return the publisher name of the virtual machine extension image this extension is created from */ @@ -56,6 +51,16 @@ public interface VirtualMachineExtension extends */ VirtualMachineExtensionInstanceView instanceView(); + /** + * @return the tags for this virtual machine extension + */ + Map tags(); + + /** + * @return the provisioning state of this virtual machine extension + */ + String provisioningState(); + /** * Grouping of virtual machine extension definition stages as a part of parent virtual machine definition. */ diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java index 8ad31f0f08b8..885c614ea485 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java @@ -1,40 +1,52 @@ package com.microsoft.azure.management.compute.implementation; -import com.microsoft.azure.management.resources.fluentcore.arm.models.ChildResource; -import com.microsoft.azure.management.resources.fluentcore.arm.models.Resource; -import com.microsoft.azure.management.resources.fluentcore.arm.models.implementation.ResourceImpl; +import com.microsoft.azure.management.compute.ExternalChildResource; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.IndexableRefreshableWrapperImpl; +import rx.Observable; /** * Externalized child resource abstract implementation. + * * @param the fluent model type of the child resource - * @param Azure inner resource class type - * @param the implementation type of the fluent model type - * @param the parent Azure resource class type + * @param Azure inner resource class type representing this child resource + * @param the parent Azure resource class type of this child resource */ -public abstract class ExternalChildResourceImpl< - FluentModelT extends Resource & ChildResource, - InnerModelT extends com.microsoft.azure.Resource, - FluentModelImplT extends ExternalChildResourceImpl, - ParentT extends Resource> +abstract class ExternalChildResourceImpl< + FluentModelT extends ExternalChildResource, + InnerModelT, + ParentImplT> extends - ResourceImpl { - protected ParentT parent; + IndexableRefreshableWrapperImpl { private State state = State.None; + private final String name; + protected final ParentImplT parent; - protected ExternalChildResourceImpl(String name, ParentT parent, InnerModelT inner) { - super(name, inner); + /** + * Creates an external child resource. + * + * @param name the name of this external child resource + * @param parent reference to the parent of this external child resource + * @param innerObject reference to the inner object representing this external child resource + */ + protected ExternalChildResourceImpl(String name, ParentImplT parent, InnerModelT innerObject) { + super(innerObject); + this.name = name; this.parent = parent; } + public String name() { + return this.name; + } + /** - * @return the state of this child resource + * @return the in-memory state of this child resource */ public State state() { return this.state; } /** - * Update the state. + * Update the in-memory state. * * @param newState the new state of this child resource */ @@ -42,6 +54,20 @@ public void setState(State newState) { this.state = newState; } + /** + * Creates or update this external child resource. + * + * @return the observable to track the create or update action + */ + public abstract Observable setAsync(); + + /** + * Delete this external child resource. + * + * @return the observable to track the delete action. + */ + public abstract Observable deleteAsync(); + /** * The possible state of an child resource in-memory. */ diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java new file mode 100644 index 000000000000..620fbe694987 --- /dev/null +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -0,0 +1,265 @@ +package com.microsoft.azure.management.compute.implementation; + +import com.microsoft.azure.management.compute.ExternalChildResource; +import rx.Observable; +import rx.Observer; +import rx.functions.Action1; +import rx.functions.Func1; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Externalized child resource collection abstract implementation. + * + * @param the type of the external child resource implementation + * @param the type of the external child resource + * @param the type of the external child resource inner model + * @param the type of parent of the external child resource collection + */ +abstract class ExternalChildResourcesImpl< + FluentModelTImpl extends ExternalChildResourceImpl, + FluentModelT extends ExternalChildResource, + InnerModelT, + ParentImplT> { + private final ParentImplT parent; + private final String parentResourceName; + private final String childResourceName; + private boolean requireRefresh = false; + private ConcurrentMap collection = new ConcurrentHashMap<>(); + + /** + * Creates a new ExternalChildResourcesImpl. + * + * @param parent the parent Azure resource + * @param parentResourceName the parent resource name + * @param childResourceName the child resource name + */ + protected ExternalChildResourcesImpl(ParentImplT parent, String parentResourceName, String childResourceName) { + this.parent = parent; + this.parentResourceName = parentResourceName; + this.childResourceName = childResourceName; + this.initializeCollection(false); + } + + /** + * Refresh the collection from the parent. + */ + public void refresh() { + initializeCollection(false); + } + + /** + * Commits the changes in the external child resource collection. + * + * @return the stream of updated extensions + */ + public Observable commitAsync() { + final ExternalChildResourcesImpl self = this; + List items = new ArrayList<>(); + for (FluentModelTImpl extension : this.collection.values()) { + items.add(extension); + } + + Observable deleteStream = Observable.from(items) + .filter(new Func1() { + @Override + public Boolean call(FluentModelTImpl childResource) { + return childResource.state() == ExternalChildResourceImpl.State.ToBeRemoved; + } + }).flatMap(new Func1>() { + @Override + public Observable call(final FluentModelTImpl childResource) { + return childResource.deleteAsync() + .map(new Func1() { + @Override + public FluentModelTImpl call(Void response) { + self.collection.remove(childResource.name()); + return childResource; + } + }); + } + }); + + Observable setStream = Observable.from(items) + .filter(new Func1() { + @Override + public Boolean call(FluentModelTImpl childResource) { + return childResource.state() == ExternalChildResourceImpl.State.ToBeUpdated + || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated; + } + }).flatMap(new Func1>() { + @Override + public Observable call(final FluentModelTImpl childResource) { + return childResource.setAsync() + .map(new Func1() { + @Override + public FluentModelTImpl call(FluentModelT e) { + childResource.setState(ExternalChildResourceImpl.State.None); + return childResource; + } + }); + } + }); + + Observable mergedStream = deleteStream.mergeWith(setStream) + .filter(new Func1() { + @Override + public Boolean call(FluentModelTImpl childResource) { + return childResource.state() == ExternalChildResourceImpl.State.None; + } + }).doOnError(new Action1() { + @Override + public void call(Throwable throwable) { + initializeCollection(true); + } + }); + + return mergedStream; + } + + /** + * @return the parent Azure resource of the external child resource + */ + protected ParentImplT parent() { + return parent; + } + + /** + * @return the collection + */ + protected Map collection() { + return this.collection; + } + + /** + * Prepare for definition of a new child resource. + * + * @param name the name for the new child resource + * @return the child resource + */ + protected FluentModelTImpl prepareDefine(String name) { + this.checkRefreshRequired(); + if (find(name) != null) { + throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' already exists"); + } + FluentModelTImpl childResource = newChildResource(name); + childResource.setState(VirtualMachineExtensionImpl.State.ToBeCreated); + return childResource; + } + + /** + * Prepare for a child resource update. + * + * @param name the name of the child resource + * @return the child resource + */ + protected FluentModelTImpl prepareUpdate(String name) { + this.checkRefreshRequired(); + FluentModelTImpl childResource = find(name); + if (childResource == null + || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { + throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' not found"); + } + if (childResource.state() == VirtualMachineExtensionImpl.State.ToBeRemoved) { + throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' is marked for deletion"); + } + childResource.setState(VirtualMachineExtensionImpl.State.ToBeUpdated); + return childResource; + } + + /** + * Mark the child resource with given name as to be removed. + * + * @param name the name of the child resource + */ + protected void prepareRemove(String name) { + this.checkRefreshRequired(); + FluentModelTImpl childResource = find(name); + if (childResource == null + || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { + throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' not found"); + } + childResource.setState(VirtualMachineExtensionImpl.State.ToBeRemoved); + } + + /** + * Adds a child resource to the collection. + * + * @param childResource the child resource + */ + protected void addChildResource(FluentModelTImpl childResource) { + this.collection.put(childResource.name(), childResource); + } + + /** + * Gets the list of external child resources. + * + * @param requireRefresh true if the child resource collection needs to be refreshed + * @return the list of external child resources + */ + protected abstract Observable listChildResourcesAsync(boolean requireRefresh); + + /** + * Creates a new external child resource. + * + * @param name the name for the new child resource + * @return the new child resource + */ + protected abstract FluentModelTImpl newChildResource(String name); + + /** + * Finds a child resource with the given name. + * + * @param name the child resource name + * @return null if no child resource exists with the given name else the child resource + */ + private FluentModelTImpl find(String name) { + for (Map.Entry entry : this.collection.entrySet()) { + if (entry.getKey().equalsIgnoreCase(name)) { + return entry.getValue(); + } + } + return null; + } + + /** + * Initializes the child resource collection. + * + * @param doRefresh true if inner collection required to be refreshed + */ + private void initializeCollection(boolean doRefresh) { + this.collection.clear(); + final ExternalChildResourcesImpl self = this; + this.listChildResourcesAsync(doRefresh) + .toBlocking() + .subscribe(new Observer() { + @Override + public void onCompleted() { + self.requireRefresh = false; + } + + @Override + public void onError(Throwable throwable) { + self.requireRefresh = true; + } + + @Override + public void onNext(FluentModelTImpl childResource) { + self.collection.put(childResource.name(), childResource); + } + }); + } + + /** + * Checks collection refresh is required if yes throw exception. + */ + private void checkRefreshRequired() { + if (this.requireRefresh) { + throw new RuntimeException("The parent '" + parentResourceName + "' needs to be refreshed before adding '" + childResourceName + "'"); + } + } +} diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java index 0c785eb4f6a0..ee3a3ff6ceee 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -6,11 +6,13 @@ import com.microsoft.azure.management.compute.VirtualMachineExtensionInstanceView; import com.microsoft.rest.ServiceResponse; import rx.Observable; +import rx.functions.Func1; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.TreeMap; /** * Implementation of {@link VirtualMachineExtension}. @@ -18,7 +20,6 @@ class VirtualMachineExtensionImpl extends ExternalChildResourceImpl implements VirtualMachineExtension, VirtualMachineExtension.Definition, @@ -58,6 +59,11 @@ protected static VirtualMachineExtensionImpl newVirtualMachineExtension(String n return extension; } + @Override + public String id() { + return this.inner().id(); + } + @Override public String publisherName() { return this.inner().publisher(); @@ -94,30 +100,17 @@ public VirtualMachineExtensionInstanceView instanceView() { } @Override - public VirtualMachineExtension refresh() throws Exception { - ServiceResponse response = - this.client.get(this.parent.resourceGroupName(), this.parent.name(), this.name()); - this.setInner(response.getBody()); - return this; - } - - @Override - public Observable applyAsync() { - return createResourceAsync(); - } - - @Override - public Observable createResourceAsync() { - return this.client.createOrUpdateAsync(this.parent.resourceGroupName(), - this.parent.name(), - this.name(), - this.inner()) - .map(innerToFluentMap(this)); + public Map tags() { + Map tags = this.inner().getTags(); + if (tags == null) { + tags = new TreeMap<>(); + } + return Collections.unmodifiableMap(tags); } @Override - public VirtualMachineExtension createResource() throws Exception { - return createResourceAsync().toBlocking().first(); + public String provisioningState() { + return this.inner().provisioningState(); } @Override @@ -184,6 +177,24 @@ public VirtualMachineExtensionImpl withVersion(String extensionImageVersionName) return this; } + @Override + public final VirtualMachineExtensionImpl withTags(Map tags) { + this.inner().withTags(new HashMap<>(tags)); + return this; + } + + @Override + public final VirtualMachineExtensionImpl withTag(String key, String value) { + this.inner().getTags().put(key, value); + return this; + } + + @Override + public final VirtualMachineExtensionImpl withoutTag(String key) { + this.inner().getTags().remove(key); + return this; + } + @Override public VirtualMachineImpl parent() { this.nullifySettingsIfEmpty(); @@ -196,6 +207,46 @@ public VirtualMachineImpl attach() { return this.parent.withExtension(this); } + @Override + public VirtualMachineExtensionImpl refresh() throws Exception { + ServiceResponse response = + this.client.get(this.parent.resourceGroupName(), this.parent.name(), this.name()); + this.setInner(response.getBody()); + return this; + } + + // Implementation of ExternalChildResourceImpl setAsync and deleteAsync + // + @Override + public Observable setAsync() { + final VirtualMachineExtensionImpl self = this; + return this.client.createOrUpdateAsync(this.parent.resourceGroupName(), + this.parent.name(), + this.name(), + this.inner()) + .map(new Func1, VirtualMachineExtension>() { + @Override + public VirtualMachineExtension call(ServiceResponse response) { + self.setInner(response.getBody()); + return self; + } + }); + } + + @Override + public Observable deleteAsync() { + return this.client.deleteAsync(this.parent.resourceGroupName(), + this.parent.name(), + this.name()).map(new Func1, Void>() { + @Override + public Void call(ServiceResponse voidServiceResponse) { + return voidServiceResponse.getBody(); + } + }); + } + + // Helper methods + // private void nullifySettingsIfEmpty() { if (this.publicSettings.size() == 0) { this.inner().withSettings(null); diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java index 3e6f49ef83fd..449af08b938a 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java @@ -1,51 +1,40 @@ package com.microsoft.azure.management.compute.implementation; - import com.microsoft.azure.management.compute.VirtualMachineExtension; -import com.microsoft.rest.ServiceResponse; import rx.Observable; -import rx.functions.Action1; import rx.functions.Func1; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; /** * Represents a extension collection associated with a virtual machine. */ -class VirtualMachineExtensionsImpl { +class VirtualMachineExtensionsImpl extends + ExternalChildResourcesImpl { private final VirtualMachineExtensionsInner client; - private final VirtualMachineImpl parent; - private ConcurrentMap extensions; - private boolean requireRefresh = false; + /** + * Creates new VirtualMachineExtensionsImpl. + * + * @param client the client to perform REST calls on extensions + * @param parent the parent virtual machine of the extensions + */ VirtualMachineExtensionsImpl(VirtualMachineExtensionsInner client, VirtualMachineImpl parent) { - this.extensions = new ConcurrentHashMap<>(); + super(parent, "VirtualMachine", "VirtualMachineExtension"); this.client = client; - this.parent = parent; - this.initializeExtensionsFromParent(); } /** - * @return the extensions as a name value map + * @return the extension as a map indexed by name. */ - Map asMap() { - if (requireRefresh) { - try { - parent.refresh(); - this.requireRefresh = false; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - initializeExtensionsFromParent(); - } + public Map asMap() { Map result = new HashMap<>(); - for (Map.Entry extensionEntry : this.extensions.entrySet()) { - result.put(extensionEntry.getKey(), extensionEntry.getValue()); + for (Map.Entry entry : this.collection().entrySet()) { + result.put(entry.getKey(), entry.getValue()); } return Collections.unmodifiableMap(result); } @@ -56,14 +45,8 @@ Map asMap() { * @param name the reference name of the extension to be added * @return the extension */ - VirtualMachineExtensionImpl define(String name) { - if (findExtension(name) != null) { - throw new IllegalArgumentException("An extension with name '" + name + "' already exists"); - } - VirtualMachineExtensionImpl extension = VirtualMachineExtensionImpl - .newVirtualMachineExtension(name, this.parent, this.client); - extension.setState(VirtualMachineExtensionImpl.State.ToBeCreated); - return extension; + public VirtualMachineExtensionImpl define(String name) { + return this.prepareDefine(name); } /** @@ -72,17 +55,8 @@ VirtualMachineExtensionImpl define(String name) { * @param name the reference name of the extension to be updated * @return the extension */ - VirtualMachineExtensionImpl update(String name) { - VirtualMachineExtensionImpl extension = findExtension(name); - if (extension == null - || extension.state() == VirtualMachineExtensionImpl.State.ToBeCreated) { - throw new IllegalArgumentException("An extension with name '" + name + "' not found"); - } - if (extension.state() == VirtualMachineExtensionImpl.State.ToBeRemoved) { - throw new IllegalArgumentException("An extension with name '" + name + "' is marked for deletion"); - } - extension.setState(VirtualMachineExtensionImpl.State.ToBeUpdated); - return extension; + public VirtualMachineExtensionImpl update(String name) { + return this.prepareUpdate(name); } /** @@ -90,116 +64,50 @@ VirtualMachineExtensionImpl update(String name) { * * @param name the reference name of the extension to be removed */ - void remove(String name) { - VirtualMachineExtensionImpl extension = findExtension(name); - if (extension == null - || extension.state() == VirtualMachineExtensionImpl.State.ToBeCreated) { - throw new IllegalArgumentException("An extension with name '" + name + "' not found"); - } - extension.setState(VirtualMachineExtensionImpl.State.ToBeRemoved); - } - - void addExtension(VirtualMachineExtensionImpl extension) { - this.extensions.put(extension.name(), extension); + public void remove(String name) { + this.prepareRemove(name); } /** - * Commits the changes in the extension collection. + * Adds the extension to the collection. * - * @return the stream of updated extensions + * @param extension the extension */ - Observable commitAsync() { - final VirtualMachineExtensionsImpl self = this; - List items = new ArrayList<>(); - for (VirtualMachineExtensionImpl extension : this.extensions.values()) { - items.add(extension); - } - - Observable deleteStream = Observable.from(items) - .filter(new Func1() { - @Override - public Boolean call(VirtualMachineExtensionImpl extension) { - return extension.state() == ExternalChildResourceImpl.State.ToBeRemoved; - } - }).flatMap(new Func1>() { - @Override - public Observable call(final VirtualMachineExtensionImpl extension) { - - return self.client.deleteAsync(self.parent.resourceGroupName(), - self.parent.name(), - extension.name()) - .map(new Func1, VirtualMachineExtensionImpl>() { - @Override - public VirtualMachineExtensionImpl call(ServiceResponse response) { - self.extensions.remove(extension.name()); - return extension; - } - }); - } - }); + public void addExtension(VirtualMachineExtensionImpl extension) { + this.addChildResource(extension); + } - Observable setStream = Observable.from(items) - .filter(new Func1() { - @Override - public Boolean call(VirtualMachineExtensionImpl extension) { - return extension.state() == ExternalChildResourceImpl.State.ToBeUpdated - || extension.state() == ExternalChildResourceImpl.State.ToBeCreated; - } - }).flatMap(new Func1>() { - @Override - public Observable call(final VirtualMachineExtensionImpl extension) { - return extension.createResourceAsync() - .map(new Func1() { - @Override - public VirtualMachineExtensionImpl call(VirtualMachineExtension e) { - extension.setState(ExternalChildResourceImpl.State.None); - return extension; - } - }); + @Override + protected Observable listChildResourcesAsync(boolean requireRefresh) { + if (requireRefresh) { + try { + parent().refresh(); // This is sync + } catch (Exception exception) { + return Observable.error(exception); } - }); + } - return deleteStream.mergeWith(setStream) - .filter(new Func1() { + if (parent().inner().resources() == null) { + return Observable.empty(); + } + + final VirtualMachineExtensionsImpl self = this; + return Observable.from(this.parent().inner().resources()) + .map(new Func1() { @Override - public Boolean call(VirtualMachineExtensionImpl extension) { - return extension.state() == ExternalChildResourceImpl.State.None; + public VirtualMachineExtensionImpl call(VirtualMachineExtensionInner inner) { + return new VirtualMachineExtensionImpl(inner.name(), + self.parent(), + inner, + self.client); } - }).doOnError(new Action1() { - @Override - public void call(Throwable throwable) { - self.extensions.clear(); - self.requireRefresh = true; - } - }); - } - - /** - * Populate the collection from the parent. - */ - private void initializeExtensionsFromParent() { - if (parent.inner().resources() != null) { - for (VirtualMachineExtensionInner innerExtension : this.parent.inner().resources()) { - this.extensions.put(innerExtension.name(), - new VirtualMachineExtensionImpl(innerExtension.name(), - this.parent, - innerExtension, - this.client)); - } - } + }); } - /** - * - * @param name the extension to lookup - * @return the extension - */ - private VirtualMachineExtensionImpl findExtension(String name) { - for (Map.Entry extensionEntry : this.extensions.entrySet()) { - if (extensionEntry.getKey().equalsIgnoreCase(name)) { - return extensionEntry.getValue(); - } - } - return null; + @Override + protected VirtualMachineExtensionImpl newChildResource(String name) { + VirtualMachineExtensionImpl extension = VirtualMachineExtensionImpl + .newVirtualMachineExtension(name, this.parent(), this.client); + return extension; } } diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java index 72d596033a58..2d2d71a1cd69 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java @@ -151,6 +151,7 @@ public VirtualMachine refresh() throws Exception { this.setInner(response.getBody()); clearCachedRelatedResources(); initializeDataDisks(); + this.virtualMachineExtensions.refresh(); return this; } From 53797174045a963aa2faacd8191e80a6326ee75f Mon Sep 17 00:00:00 2001 From: anuchan Date: Thu, 1 Sep 2016 11:43:27 -0700 Subject: [PATCH 05/18] allow inprogress operations to finish, aggregate any exceptions and report them only after the completion of all operations --- .../ExternalChildResourcesImpl.java | 121 ++++++++++-------- .../VirtualMachineExtensionsImpl.java | 38 ++---- 2 files changed, 77 insertions(+), 82 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index 620fbe694987..78662e77aa28 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -2,11 +2,13 @@ import com.microsoft.azure.management.compute.ExternalChildResource; import rx.Observable; -import rx.Observer; -import rx.functions.Action1; +import rx.exceptions.CompositeException; +import rx.functions.Action0; import rx.functions.Func1; +import rx.subjects.PublishSubject; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -15,10 +17,10 @@ /** * Externalized child resource collection abstract implementation. * - * @param the type of the external child resource implementation + * @param the implementation of {@param FluentModelT} * @param the type of the external child resource - * @param the type of the external child resource inner model - * @param the type of parent of the external child resource collection + * @param the type of the external child resource inner + * @param the type of parent of the external child resources */ abstract class ExternalChildResourcesImpl< FluentModelTImpl extends ExternalChildResourceImpl, @@ -26,36 +28,32 @@ abstract class ExternalChildResourcesImpl< InnerModelT, ParentImplT> { private final ParentImplT parent; - private final String parentResourceName; private final String childResourceName; - private boolean requireRefresh = false; private ConcurrentMap collection = new ConcurrentHashMap<>(); /** * Creates a new ExternalChildResourcesImpl. * * @param parent the parent Azure resource - * @param parentResourceName the parent resource name * @param childResourceName the child resource name */ - protected ExternalChildResourcesImpl(ParentImplT parent, String parentResourceName, String childResourceName) { + protected ExternalChildResourcesImpl(ParentImplT parent, String childResourceName) { this.parent = parent; - this.parentResourceName = parentResourceName; this.childResourceName = childResourceName; - this.initializeCollection(false); + this.initializeCollection(); } /** * Refresh the collection from the parent. */ public void refresh() { - initializeCollection(false); + initializeCollection(); } /** * Commits the changes in the external child resource collection. * - * @return the stream of updated extensions + * @return the stream of committed resources */ public Observable commitAsync() { final ExternalChildResourcesImpl self = this; @@ -64,6 +62,9 @@ public Observable commitAsync() { items.add(extension); } + final List exceptionsList = Collections.synchronizedList(new ArrayList()); + final PublishSubject exceptionSubject = PublishSubject.create(); + Observable deleteStream = Observable.from(items) .filter(new Func1() { @Override @@ -80,16 +81,22 @@ public FluentModelTImpl call(Void response) { self.collection.remove(childResource.name()); return childResource; } + }).onErrorResumeNext(new Func1>() { + @Override + public Observable call(Throwable throwable) { + childResource.setState(ExternalChildResourceImpl.State.None); + exceptionsList.add(throwable); + return Observable.empty(); + } }); } }); - Observable setStream = Observable.from(items) + Observable createStream = Observable.from(items) .filter(new Func1() { @Override public Boolean call(FluentModelTImpl childResource) { - return childResource.state() == ExternalChildResourceImpl.State.ToBeUpdated - || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated; + return childResource.state() == ExternalChildResourceImpl.State.ToBeCreated; } }).flatMap(new Func1>() { @Override @@ -101,23 +108,56 @@ public FluentModelTImpl call(FluentModelT e) { childResource.setState(ExternalChildResourceImpl.State.None); return childResource; } + }).onErrorResumeNext(new Func1>() { + @Override + public Observable call(Throwable throwable) { + self.collection.remove(childResource.name()); + exceptionsList.add(throwable); + return Observable.empty(); + } }); } }); - Observable mergedStream = deleteStream.mergeWith(setStream) + Observable updateStream = Observable.from(items) .filter(new Func1() { @Override public Boolean call(FluentModelTImpl childResource) { - return childResource.state() == ExternalChildResourceImpl.State.None; + return childResource.state() == ExternalChildResourceImpl.State.ToBeUpdated; } - }).doOnError(new Action1() { + }).flatMap(new Func1>() { @Override - public void call(Throwable throwable) { - initializeCollection(true); + public Observable call(final FluentModelTImpl childResource) { + return childResource.setAsync() + .map(new Func1() { + @Override + public FluentModelTImpl call(FluentModelT e) { + childResource.setState(ExternalChildResourceImpl.State.None); + return childResource; + } + }).onErrorResumeNext(new Func1>() { + @Override + public Observable call(Throwable throwable) { + exceptionsList.add(throwable); + return Observable.empty(); + } + }); } }); + Observable operationsStream = Observable.merge(deleteStream, createStream, updateStream); + operationsStream.doOnTerminate(new Action0() { + @Override + public void call() { + if (exceptionsList.isEmpty()) { + exceptionSubject.onCompleted(); + } else { + exceptionSubject.onError(new CompositeException(exceptionsList)); + } + } + }); + + Observable mergedStream = Observable.mergeDelayError(operationsStream, exceptionSubject); return mergedStream; } @@ -142,7 +182,6 @@ protected Map collection() { * @return the child resource */ protected FluentModelTImpl prepareDefine(String name) { - this.checkRefreshRequired(); if (find(name) != null) { throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' already exists"); } @@ -158,7 +197,6 @@ protected FluentModelTImpl prepareDefine(String name) { * @return the child resource */ protected FluentModelTImpl prepareUpdate(String name) { - this.checkRefreshRequired(); FluentModelTImpl childResource = find(name); if (childResource == null || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { @@ -177,7 +215,6 @@ protected FluentModelTImpl prepareUpdate(String name) { * @param name the name of the child resource */ protected void prepareRemove(String name) { - this.checkRefreshRequired(); FluentModelTImpl childResource = find(name); if (childResource == null || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { @@ -198,10 +235,9 @@ protected void addChildResource(FluentModelTImpl childResource) { /** * Gets the list of external child resources. * - * @param requireRefresh true if the child resource collection needs to be refreshed * @return the list of external child resources */ - protected abstract Observable listChildResourcesAsync(boolean requireRefresh); + protected abstract List listChildResources(); /** * Creates a new external child resource. @@ -228,38 +264,11 @@ private FluentModelTImpl find(String name) { /** * Initializes the child resource collection. - * - * @param doRefresh true if inner collection required to be refreshed */ - private void initializeCollection(boolean doRefresh) { + private void initializeCollection() { this.collection.clear(); - final ExternalChildResourcesImpl self = this; - this.listChildResourcesAsync(doRefresh) - .toBlocking() - .subscribe(new Observer() { - @Override - public void onCompleted() { - self.requireRefresh = false; - } - - @Override - public void onError(Throwable throwable) { - self.requireRefresh = true; - } - - @Override - public void onNext(FluentModelTImpl childResource) { - self.collection.put(childResource.name(), childResource); - } - }); - } - - /** - * Checks collection refresh is required if yes throw exception. - */ - private void checkRefreshRequired() { - if (this.requireRefresh) { - throw new RuntimeException("The parent '" + parentResourceName + "' needs to be refreshed before adding '" + childResourceName + "'"); + for (FluentModelTImpl childResource : this.listChildResources()) { + this.collection.put(childResource.name(), childResource); } } } diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java index 449af08b938a..08c674430a97 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java @@ -1,10 +1,9 @@ package com.microsoft.azure.management.compute.implementation; import com.microsoft.azure.management.compute.VirtualMachineExtension; -import rx.Observable; -import rx.functions.Func1; - +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -24,7 +23,7 @@ class VirtualMachineExtensionsImpl extends * @param parent the parent virtual machine of the extensions */ VirtualMachineExtensionsImpl(VirtualMachineExtensionsInner client, VirtualMachineImpl parent) { - super(parent, "VirtualMachine", "VirtualMachineExtension"); + super(parent, "VirtualMachineExtension"); this.client = client; } @@ -78,30 +77,17 @@ public void addExtension(VirtualMachineExtensionImpl extension) { } @Override - protected Observable listChildResourcesAsync(boolean requireRefresh) { - if (requireRefresh) { - try { - parent().refresh(); // This is sync - } catch (Exception exception) { - return Observable.error(exception); + protected List listChildResources() { + List childResources = new ArrayList<>(); + if (parent().inner().resources() != null) { + for (VirtualMachineExtensionInner inner : parent().inner().resources()) { + childResources.add(new VirtualMachineExtensionImpl(inner.name(), + this.parent(), + inner, + this.client)); } } - - if (parent().inner().resources() == null) { - return Observable.empty(); - } - - final VirtualMachineExtensionsImpl self = this; - return Observable.from(this.parent().inner().resources()) - .map(new Func1() { - @Override - public VirtualMachineExtensionImpl call(VirtualMachineExtensionInner inner) { - return new VirtualMachineExtensionImpl(inner.name(), - self.parent(), - inner, - self.client); - } - }); + return childResources; } @Override From 8450f8ee026724b02b009362834be3716bdc2d2b Mon Sep 17 00:00:00 2001 From: anuchan Date: Fri, 2 Sep 2016 15:44:56 -0700 Subject: [PATCH 06/18] use do* methods to perform actions that has side effects --- .../ExternalChildResourcesImpl.java | 96 ++++++++++++++----- 1 file changed, 71 insertions(+), 25 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index 78662e77aa28..597177b686d2 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -4,6 +4,9 @@ import rx.Observable; import rx.exceptions.CompositeException; import rx.functions.Action0; +import rx.functions.Action1; +import rx.functions.Action2; +import rx.functions.Func0; import rx.functions.Func1; import rx.subjects.PublishSubject; @@ -52,8 +55,12 @@ public void refresh() { /** * Commits the changes in the external child resource collection. + *

+ * This method returns a stream where onNext will be called for each successfully committed resource + * followed by 'one call to onCompleted or one call to onError' with a {@link CompositeException } containing + * the list of exceptions where each exception describes the reason for failure in committing a resource. * - * @return the stream of committed resources + * @return the observable stream */ public Observable commitAsync() { final ExternalChildResourcesImpl self = this; @@ -63,8 +70,6 @@ public Observable commitAsync() { } final List exceptionsList = Collections.synchronizedList(new ArrayList()); - final PublishSubject exceptionSubject = PublishSubject.create(); - Observable deleteStream = Observable.from(items) .filter(new Func1() { @Override @@ -78,13 +83,18 @@ public Observable call(final FluentModelTImpl childResource) { .map(new Func1() { @Override public FluentModelTImpl call(Void response) { - self.collection.remove(childResource.name()); return childResource; } - }).onErrorResumeNext(new Func1>() { + }).doOnNext(new Action1() { @Override - public Observable call(Throwable throwable) { + public void call(FluentModelTImpl childResource) { childResource.setState(ExternalChildResourceImpl.State.None); + self.collection.remove(childResource.name()); + } + }) + .onErrorResumeNext(new Func1>() { + @Override + public Observable call(Throwable throwable) { exceptionsList.add(throwable); return Observable.empty(); } @@ -104,13 +114,19 @@ public Observable call(final FluentModelTImpl childResource) { return childResource.setAsync() .map(new Func1() { @Override - public FluentModelTImpl call(FluentModelT e) { - childResource.setState(ExternalChildResourceImpl.State.None); + public FluentModelTImpl call(FluentModelT fluentModelT) { return childResource; } - }).onErrorResumeNext(new Func1>() { + }) + .doOnNext(new Action1() { + @Override + public void call(FluentModelTImpl fluentModelT) { + childResource.setState(ExternalChildResourceImpl.State.None); + } + }) + .onErrorResumeNext(new Func1>() { @Override - public Observable call(Throwable throwable) { + public Observable call(Throwable throwable) { self.collection.remove(childResource.name()); exceptionsList.add(throwable); return Observable.empty(); @@ -132,12 +148,18 @@ public Observable call(final FluentModelTImpl childResource) { .map(new Func1() { @Override public FluentModelTImpl call(FluentModelT e) { - childResource.setState(ExternalChildResourceImpl.State.None); return childResource; } - }).onErrorResumeNext(new Func1>() { + }) + .doOnNext(new Action1() { @Override - public Observable call(Throwable throwable) { + public void call(FluentModelTImpl childResource) { + childResource.setState(ExternalChildResourceImpl.State.None); + } + }) + .onErrorResumeNext(new Func1>() { + @Override + public Observable call(Throwable throwable) { exceptionsList.add(throwable); return Observable.empty(); } @@ -145,20 +167,44 @@ public Observable call(Throwable throwable) { } }); - Observable operationsStream = Observable.merge(deleteStream, createStream, updateStream); - operationsStream.doOnTerminate(new Action0() { - @Override - public void call() { - if (exceptionsList.isEmpty()) { - exceptionSubject.onCompleted(); - } else { - exceptionSubject.onError(new CompositeException(exceptionsList)); + final PublishSubject aggregatedErrorStream = PublishSubject.create(); + Observable operationsStream = Observable.merge(deleteStream, + createStream, + updateStream).doOnTerminate(new Action0() { + @Override + public void call() { + if (exceptionsList.isEmpty()) { + aggregatedErrorStream.onCompleted(); + } else { + aggregatedErrorStream.onError(new CompositeException(exceptionsList)); + } } - } - }); + }); + + Observable stream = Observable.concat(operationsStream, aggregatedErrorStream); + return stream; + } - Observable mergedStream = Observable.mergeDelayError(operationsStream, exceptionSubject); - return mergedStream; + /** + * Commits the changes in the external child resource collection. + *

+ * This method returns a stream where either onError will be called with {@link CompositeException} if some + * resources are failed to commit or onNext will be called if all resources committed successfully + * + * @return the observable stream + */ + public Observable> commitAndGetAllAsync() { + return commitAsync().collect( + new Func0>() { + public List call() { + return new ArrayList<>(); + } + }, + new Action2, FluentModelTImpl>() { + public void call(List state, FluentModelTImpl item) { + state.add(item); + } + }); } /** From 8c4ae826731366db1196efaec5df6b7088c7db60 Mon Sep 17 00:00:00 2001 From: anuchan Date: Fri, 2 Sep 2016 16:07:30 -0700 Subject: [PATCH 07/18] Using explict methods for create and update (instead of set that was used for both) this is to accomodate childresources having different API methods for create and update --- .../ExternalChildResourceImpl.java | 13 +++++++--- .../ExternalChildResourcesImpl.java | 24 ++++++++++--------- .../VirtualMachineExtensionImpl.java | 9 +++++-- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java index 885c614ea485..5218886b2d70 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java @@ -55,11 +55,18 @@ public void setState(State newState) { } /** - * Creates or update this external child resource. + * Creates this external child resource. * - * @return the observable to track the create or update action + * @return the observable to track the create action */ - public abstract Observable setAsync(); + public abstract Observable createAsync(); + + /** + * Update this external child resource. + * + * @return the observable to track the update action + */ + public abstract Observable updateAsync(); /** * Delete this external child resource. diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index 597177b686d2..d151c0ee4095 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -56,9 +56,10 @@ public void refresh() { /** * Commits the changes in the external child resource collection. *

- * This method returns a stream where onNext will be called for each successfully committed resource - * followed by 'one call to onCompleted or one call to onError' with a {@link CompositeException } containing - * the list of exceptions where each exception describes the reason for failure in committing a resource. + * This method returns an observable stream, its observer's onNext will be called for each successfully + * committed resource followed by 'one call to onCompleted' or one call to onError with a + * {@link CompositeException } containing the list of exceptions where each exception describes the reason + * for failure of a resource commit. * * @return the observable stream */ @@ -111,7 +112,7 @@ public Boolean call(FluentModelTImpl childResource) { }).flatMap(new Func1>() { @Override public Observable call(final FluentModelTImpl childResource) { - return childResource.setAsync() + return childResource.createAsync() .map(new Func1() { @Override public FluentModelTImpl call(FluentModelT fluentModelT) { @@ -144,7 +145,7 @@ public Boolean call(FluentModelTImpl childResource) { }).flatMap(new Func1>() { @Override public Observable call(final FluentModelTImpl childResource) { - return childResource.setAsync() + return childResource.updateAsync() .map(new Func1() { @Override public FluentModelTImpl call(FluentModelT e) { @@ -188,8 +189,9 @@ public void call() { /** * Commits the changes in the external child resource collection. *

- * This method returns a stream where either onError will be called with {@link CompositeException} if some - * resources are failed to commit or onNext will be called if all resources committed successfully + * This method returns a observable stream, either its observer's onError will be called with + * {@link CompositeException} if some resources failed to commit or onNext will be called if all resources + * committed successfully. * * @return the observable stream */ @@ -232,7 +234,7 @@ protected FluentModelTImpl prepareDefine(String name) { throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' already exists"); } FluentModelTImpl childResource = newChildResource(name); - childResource.setState(VirtualMachineExtensionImpl.State.ToBeCreated); + childResource.setState(ExternalChildResourceImpl.State.ToBeCreated); return childResource; } @@ -248,10 +250,10 @@ protected FluentModelTImpl prepareUpdate(String name) { || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' not found"); } - if (childResource.state() == VirtualMachineExtensionImpl.State.ToBeRemoved) { + if (childResource.state() == ExternalChildResourceImpl.State.ToBeRemoved) { throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' is marked for deletion"); } - childResource.setState(VirtualMachineExtensionImpl.State.ToBeUpdated); + childResource.setState(ExternalChildResourceImpl.State.ToBeUpdated); return childResource; } @@ -266,7 +268,7 @@ protected void prepareRemove(String name) { || childResource.state() == ExternalChildResourceImpl.State.ToBeCreated) { throw new IllegalArgumentException("A child resource ('" + childResourceName + "') with name '" + name + "' not found"); } - childResource.setState(VirtualMachineExtensionImpl.State.ToBeRemoved); + childResource.setState(ExternalChildResourceImpl.State.ToBeRemoved); } /** diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java index ee3a3ff6ceee..cfb6aeb20289 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -215,10 +215,10 @@ public VirtualMachineExtensionImpl refresh() throws Exception { return this; } - // Implementation of ExternalChildResourceImpl setAsync and deleteAsync + // Implementation of ExternalChildResourceImpl createAsync, updateAsync and deleteAsync // @Override - public Observable setAsync() { + public Observable createAsync() { final VirtualMachineExtensionImpl self = this; return this.client.createOrUpdateAsync(this.parent.resourceGroupName(), this.parent.name(), @@ -233,6 +233,11 @@ public VirtualMachineExtension call(ServiceResponse updateAsync() { + return this.createAsync(); + } + @Override public Observable deleteAsync() { return this.client.deleteAsync(this.parent.resourceGroupName(), From b6278d3a832c55d3ffcfa7e7f5f56efc3a6f36eb Mon Sep 17 00:00:00 2001 From: anuchan Date: Fri, 2 Sep 2016 19:11:02 -0700 Subject: [PATCH 08/18] adding unit tests for externalChildResource commit actions --- .../ExternalChildResourceImpl.java | 19 +- .../ExternalChildResourcesImpl.java | 2 +- .../compute/childresource/ChickenImpl.java | 43 +++++ .../ExternalChildResourceTests.java | 168 ++++++++++++++++++ .../compute/childresource/Pullet.java | 6 + .../compute/childresource/PulletImpl.java | 99 +++++++++++ .../compute/childresource/PulletsImpl.java | 43 +++++ 7 files changed, 377 insertions(+), 3 deletions(-) create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ChickenImpl.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/Pullet.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletImpl.java create mode 100644 azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java index 5218886b2d70..057947ef0547 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java @@ -11,7 +11,7 @@ * @param Azure inner resource class type representing this child resource * @param the parent Azure resource class type of this child resource */ -abstract class ExternalChildResourceImpl< +public abstract class ExternalChildResourceImpl< FluentModelT extends ExternalChildResource, InnerModelT, ParentImplT> @@ -34,6 +34,9 @@ protected ExternalChildResourceImpl(String name, ParentImplT parent, InnerModelT this.parent = parent; } + /** + * @return the resource name + */ public String name() { return this.name; } @@ -78,10 +81,22 @@ public void setState(State newState) { /** * The possible state of an child resource in-memory. */ - enum State { + public enum State { + /** + * No action needs to be taken on resource + */ None, + /** + * Resource required to be created + */ ToBeCreated, + /** + * Resource required to be updated + */ ToBeUpdated, + /** + * Resource required to be updated + */ ToBeRemoved } } \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index d151c0ee4095..787088a8d515 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -25,7 +25,7 @@ * @param the type of the external child resource inner * @param the type of parent of the external child resources */ -abstract class ExternalChildResourcesImpl< +public abstract class ExternalChildResourcesImpl< FluentModelTImpl extends ExternalChildResourceImpl, FluentModelT extends ExternalChildResource, InnerModelT, diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ChickenImpl.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ChickenImpl.java new file mode 100644 index 000000000000..65a7b7cab15d --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ChickenImpl.java @@ -0,0 +1,43 @@ +package com.microsoft.azure.management.compute.childresource; + +import rx.Observable; +import rx.functions.Func1; + +class ChickenImpl { + private PulletsImpl pullets; + ChickenImpl() { + this.pullets = new PulletsImpl(this); + } + + PulletsImpl pullets() { + return this.pullets; + } + + ChickenImpl withPullet(PulletImpl pullet) { + this.pullets.addPullet(pullet); + return this; + } + + PulletImpl defineNewPullet(String name) { + return this.pullets.define(name); + } + + PulletImpl updatePullet(String name) { + return this.pullets.update(name); + } + + ChickenImpl withoutPullet(String name) { + this.pullets.remove(name); + return this; + } + + Observable applyAsync() { + final ChickenImpl self = this; + return this.pullets.commitAsync() + .map(new Func1() { + public ChickenImpl call(PulletImpl p) { + return self; + } + }); + } +} diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java new file mode 100644 index 000000000000..b80f4693a53f --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java @@ -0,0 +1,168 @@ +package com.microsoft.azure.management.compute.childresource; + +import com.microsoft.azure.management.compute.implementation.ExternalChildResourceImpl; +import org.junit.Assert; +import org.junit.Test; +import rx.Observer; +import rx.Subscriber; +import rx.exceptions.CompositeException; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +public class ExternalChildResourceTests { + @Test + public void noCommitIfNoChange() throws InterruptedException { + ChickenImpl chicken = new ChickenImpl(); + PulletsImpl pullets = chicken.pullets(); + final CountDownLatch monitor = new CountDownLatch(1); + pullets.commitAsync() + .subscribe(new Subscriber() { + @Override + public void onCompleted() { + monitor.countDown(); + } + + @Override + public void onError(Throwable throwable) { + monitor.countDown(); + Assert.assertTrue("nothing to commit onError should not be invoked", false); + } + + @Override + public void onNext(PulletImpl pullet) { + Assert.assertTrue("nothing to commit onNext should not be invoked", false); + } + }); + monitor.await(); + } + + @Test + public void shouldCommitCreateUpdateAndDelete() throws InterruptedException { + ChickenImpl chicken = new ChickenImpl(); + chicken + .defineNewPullet("alice") + .withAge(1) + .attach() + .updatePullet("Clover") + .withAge(2) + .attach() + .withoutPullet("Pinky"); + + final List changedPuppets = new ArrayList<>(); + final CountDownLatch monitor = new CountDownLatch(1); + PulletsImpl pullets = chicken.pullets(); + pullets.commitAsync() + .subscribe(new Observer() { + @Override + public void onCompleted() { + monitor.countDown(); + } + + @Override + public void onError(Throwable throwable) { + monitor.countDown(); + Assert.assertTrue("onError should not be invoked", false); + } + + @Override + public void onNext(PulletImpl pullet) { + changedPuppets.add(pullet); + } + }); + monitor.await(); + Assert.assertTrue(changedPuppets.size() == 3); + for (PulletImpl pullet : changedPuppets) { + Assert.assertTrue(pullet.state() == ExternalChildResourceImpl.State.None); + } + } + + @Test + public void shouldEmitErrorAfterAllSuccessfulCommit() throws InterruptedException { + ChickenImpl chicken = new ChickenImpl(); + chicken + .defineNewPullet("alice") + .withAge(1) + .withFailFlag(PulletImpl.FailFlag.OnCreate) + .attach() + .updatePullet("Clover") + .withAge(2) + .parent() + .updatePullet("Goldilocks") + .withAge(2) + .withFailFlag(PulletImpl.FailFlag.OnUpdate) + .parent() + .withoutPullet("Pinky"); + + final List changedPuppets = new ArrayList<>(); + final List throwables = new ArrayList<>(); + final CountDownLatch monitor = new CountDownLatch(1); + PulletsImpl pullets = chicken.pullets(); + pullets.commitAsync() + .subscribe(new Observer() { + @Override + public void onCompleted() { + monitor.countDown(); + Assert.assertTrue("onCompleted should not be invoked", false); + } + + @Override + public void onError(Throwable throwable) { + try { + CompositeException exception = (CompositeException) throwable; + Assert.assertNotNull(exception); + for (Throwable innerThrowable : exception.getExceptions()) { + throwables.add(innerThrowable); + } + } finally { + monitor.countDown(); + } + } + + @Override + public void onNext(PulletImpl pullet) { + changedPuppets.add(pullet); + } + }); + + monitor.await(); + Assert.assertTrue(throwables.size() == 2); + Assert.assertTrue(changedPuppets.size() == 2); + } + + @Test + public void canStreamAccumulatedResult() throws InterruptedException { + ChickenImpl chicken = new ChickenImpl(); + chicken + .defineNewPullet("alice") + .withAge(1) + .attach() + .updatePullet("Clover") + .withAge(2) + .attach() + .withoutPullet("Pinky"); + + PulletsImpl pullets = chicken.pullets(); + final CountDownLatch monitor = new CountDownLatch(1); + pullets.commitAndGetAllAsync() + .subscribe(new Subscriber>() { + @Override + public void onCompleted() { + monitor.countDown(); + } + + @Override + public void onError(Throwable throwable) { + monitor.countDown(); + Assert.assertTrue("onError should not be invoked", false); + } + + @Override + public void onNext(List pullets) { + Assert.assertTrue(pullets.size() == 3); + } + }); + monitor.await(); + } +} diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/Pullet.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/Pullet.java new file mode 100644 index 000000000000..b0ea9f4a1686 --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/Pullet.java @@ -0,0 +1,6 @@ +package com.microsoft.azure.management.compute.childresource; + +import com.microsoft.azure.management.compute.ExternalChildResource; + +interface Pullet extends ExternalChildResource { +} diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletImpl.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletImpl.java new file mode 100644 index 000000000000..9730dd90269e --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletImpl.java @@ -0,0 +1,99 @@ +package com.microsoft.azure.management.compute.childresource; + +import com.microsoft.azure.management.compute.implementation.ExternalChildResourceImpl; +import rx.Observable; +import rx.schedulers.Schedulers; + +class PulletImpl extends ExternalChildResourceImpl + implements Pullet { + Integer age; + private FailFlag failFlag = FailFlag.None; + + PulletImpl(String name, ChickenImpl parent) { + super(name, parent, new Object()); + } + + public PulletImpl withAge(Integer age) { + this.age = age; + return this; + } + + public PulletImpl withFailFlag(FailFlag failFlag) { + this.failFlag = failFlag; + return this; + } + + public ChickenImpl parent() { + return this.parent; + } + + public ChickenImpl attach() { + return this.parent.withPullet(this); + } + + @Override + public Observable createAsync() { + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + + if (this.failFlag == FailFlag.OnCreate) { + return Observable.error(new Exception("Creation of " + this.name() + " failed")); + } + + Pullet self = this; + return Observable + .just(self) + .observeOn(Schedulers.computation()); + } + + @Override + public Observable updateAsync() { + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + + if (this.failFlag == FailFlag.OnUpdate) { + return Observable.error(new Exception("Update of " + this.name() + " failed")); + } + + Pullet self = this; + return Observable + .just(self) + .observeOn(Schedulers.computation()); + } + + @Override + public Observable deleteAsync() { + try { + Thread.sleep(2000); + } catch (InterruptedException ex) { + } + + if (this.failFlag == FailFlag.OnDelete) { + return Observable.error(new Exception("Deletion of " + this.name() + " failed")); + } + + return Observable + .just(null); + } + + @Override + public PulletImpl refresh() throws Exception { + return null; + } + + @Override + public String id() { + return null; + } + + enum FailFlag { + None, + OnCreate, + OnUpdate, + OnDelete + } +} diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java new file mode 100644 index 000000000000..f2bf678034f5 --- /dev/null +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java @@ -0,0 +1,43 @@ +package com.microsoft.azure.management.compute.childresource; + +import com.microsoft.azure.management.compute.implementation.ExternalChildResourcesImpl; +import java.util.ArrayList; +import java.util.List; + +class PulletsImpl extends ExternalChildResourcesImpl { + PulletsImpl(ChickenImpl parent) { + super(parent, "Pullet"); + } + + public PulletImpl define(String name) { + return super.prepareDefine(name); + } + + public PulletImpl update(String name) { + return super.prepareUpdate(name); + } + + public void remove(String name) { + super.prepareRemove(name); + } + + public void addPullet(PulletImpl pullet) { + this.addChildResource(pullet); + } + + @Override + protected List listChildResources() { + List resources = new ArrayList<>(); + resources.add(new PulletImpl("Tilly", this.parent())); + resources.add(new PulletImpl("Clover", this.parent())); + resources.add(new PulletImpl("Savvy", this.parent())); + resources.add(new PulletImpl("Pinky", this.parent())); + resources.add(new PulletImpl("Goldilocks", this.parent())); + return resources; + } + + @Override + protected PulletImpl newChildResource(String name) { + return new PulletImpl(name, this.parent()); + } +} From 2cee3b9e5c402fba82c5bea5d2232929cd13e01a Mon Sep 17 00:00:00 2001 From: anuchan Date: Fri, 2 Sep 2016 19:12:42 -0700 Subject: [PATCH 09/18] attach -> parent --- .../compute/childresource/ExternalChildResourceTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java index b80f4693a53f..cbab39618b7a 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java @@ -47,7 +47,7 @@ public void shouldCommitCreateUpdateAndDelete() throws InterruptedException { .attach() .updatePullet("Clover") .withAge(2) - .attach() + .parent() .withoutPullet("Pinky"); final List changedPuppets = new ArrayList<>(); From d1ce9b58e1b4060cd524c829f14091a76fde787e Mon Sep 17 00:00:00 2001 From: anuchan Date: Sun, 4 Sep 2016 18:15:45 -0700 Subject: [PATCH 10/18] using extension::commitAsync after vm create, adding unit test for removing extension from vm --- .../ExternalChildResourceImpl.java | 10 ++--- .../ExternalChildResourcesImpl.java | 2 +- .../VirtualMachineExtensionImpl.java | 6 ++- .../implementation/VirtualMachineImpl.java | 11 ++++++ .../compute/ComputeManagementTestBase.java | 3 +- ...irtualMachineExtensionOperationsTests.java | 37 ++++++++++++++----- .../model/implementation/CreatorTaskItem.java | 10 ++--- .../com/microsoft/azure/TaskGroupBase.java | 33 ++++++++++++----- 8 files changed, 77 insertions(+), 35 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java index 057947ef0547..bc3fad75ddfc 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java @@ -6,7 +6,7 @@ /** * Externalized child resource abstract implementation. - * + * (Internal use only) * @param the fluent model type of the child resource * @param Azure inner resource class type representing this child resource * @param the parent Azure resource class type of this child resource @@ -83,19 +83,19 @@ public void setState(State newState) { */ public enum State { /** - * No action needs to be taken on resource + * No action needs to be taken on resource. */ None, /** - * Resource required to be created + * Resource required to be created. */ ToBeCreated, /** - * Resource required to be updated + * Resource required to be updated. */ ToBeUpdated, /** - * Resource required to be updated + * Resource required to be deleted. */ ToBeRemoved } diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index 787088a8d515..9068e14887e5 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -288,7 +288,7 @@ protected void addChildResource(FluentModelTImpl childResource) { protected abstract List listChildResources(); /** - * Creates a new external child resource. + * Gets a new external child resource model instance. * * @param name the name for the new child resource * @return the new child resource diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java index cfb6aeb20289..a2ca8022b0eb 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -52,9 +52,11 @@ class VirtualMachineExtensionImpl protected static VirtualMachineExtensionImpl newVirtualMachineExtension(String name, VirtualMachineImpl parent, VirtualMachineExtensionsInner client) { + VirtualMachineExtensionInner inner = new VirtualMachineExtensionInner(); + inner.withLocation(parent.regionName()); VirtualMachineExtensionImpl extension = new VirtualMachineExtensionImpl(name, parent, - new VirtualMachineExtensionInner(), + inner, client); return extension; } @@ -71,7 +73,7 @@ public String publisherName() { @Override public String typeName() { - return this.inner().type(); + return this.inner().virtualMachineExtensionType(); } @Override diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java index 2d2d71a1cd69..386c0d7dfec6 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java @@ -898,6 +898,17 @@ public VirtualMachine call(ServiceResponse virtualMachineIn } }); } + }).flatMap(new Func1>() { + @Override + public Observable call(VirtualMachine virtualMachine) { + return self.virtualMachineExtensions.commitAndGetAllAsync() + .map(new Func1, VirtualMachine>() { + @Override + public VirtualMachine call(List virtualMachineExtensions) { + return self; + } + }); + } }); } diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/ComputeManagementTestBase.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/ComputeManagementTestBase.java index 4c3b5d98bd8d..d592d2da2a92 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/ComputeManagementTestBase.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/ComputeManagementTestBase.java @@ -5,6 +5,7 @@ import com.microsoft.azure.management.compute.implementation.ComputeManager; import com.microsoft.azure.management.resources.implementation.ResourceManager; import com.microsoft.azure.RestClient; +import okhttp3.logging.HttpLoggingInterceptor; public abstract class ComputeManagementTestBase { protected static ResourceManager resourceManager; @@ -19,7 +20,7 @@ public static void createClients() { RestClient restClient = AzureEnvironment.AZURE.newRestClientBuilder() .withCredentials(credentials) - //.withLogLevel(HttpLoggingInterceptor.Level.BASIC) + .withLogLevel(HttpLoggingInterceptor.Level.BODY) .build(); resourceManager = ResourceManager diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java index 8092c7673148..ac2ac42d38cb 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java @@ -2,12 +2,14 @@ import com.microsoft.azure.management.resources.fluentcore.utils.ResourceNamer; import org.junit.AfterClass; +import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; +import java.util.List; public class VirtualMachineExtensionOperationsTests extends ComputeManagementTestBase { - private static final String RG_NAME = ResourceNamer.randomResourceName("jsdkunittest", 15); + private static final String RG_NAME = ResourceNamer.randomResourceName("vmexttest", 15); private static final String LOCATION = "eastus"; private static final String VMNAME = "javavm"; @@ -24,7 +26,11 @@ public static void cleanup() throws Exception { public void canInstallUpdateUninstallExtension() throws Exception { final String mySqlInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; final String installCommand = "bash install_mysql_server_5.6.sh Abc.123x("; + List fileUris = new ArrayList<>(); + fileUris.add(mySqlInstallScript); + // Create VM and add extension + // VirtualMachine vm = computeManager.virtualMachines() .define(VMNAME) .withRegion(LOCATION) @@ -37,15 +43,28 @@ public void canInstallUpdateUninstallExtension() throws Exception { .withPassword("BaR@12abc!") .withSize(VirtualMachineSizeTypes.STANDARD_D3) .defineNewExtension("CustomScriptForLinux") - .withPublisher("Microsoft.OSTCExtensions") - .withType("CustomScriptForLinux") - .withVersion("1.4") - .withAutoUpgradeMinorVersionEnabled() - .withPublicSetting("fileUris", new ArrayList() {{ - add(mySqlInstallScript); - }}) - .withPublicSetting("commandToExecute", installCommand) + .withPublisher("Microsoft.OSTCExtensions") + .withType("CustomScriptForLinux") + .withVersion("1.4") + .withAutoUpgradeMinorVersionEnabled() + .withPublicSetting("fileUris",fileUris) + .withPublicSetting("commandToExecute", installCommand) .attach() .create(); + + Assert.assertTrue(vm.extensions().size() > 0); + Assert.assertTrue(vm.extensions().containsKey("CustomScriptForLinux")); + VirtualMachineExtension customScriptExtension = vm.extensions().get("CustomScriptForLinux"); + Assert.assertEquals(customScriptExtension.publisherName(), "Microsoft.OSTCExtensions"); + Assert.assertEquals(customScriptExtension.typeName(), "CustomScriptForLinux"); + Assert.assertEquals(customScriptExtension.autoUpgradeMinorVersionEnabled(), true); + + // Remove the extension + // + vm.update() + .withoutExtension("CustomScriptForLinux") + .apply(); + + Assert.assertTrue(vm.extensions().size() == 0); } } \ No newline at end of file diff --git a/azure-mgmt-resources/src/main/java/com/microsoft/azure/management/resources/fluentcore/model/implementation/CreatorTaskItem.java b/azure-mgmt-resources/src/main/java/com/microsoft/azure/management/resources/fluentcore/model/implementation/CreatorTaskItem.java index eee27b0b5d18..ae26ea92e260 100644 --- a/azure-mgmt-resources/src/main/java/com/microsoft/azure/management/resources/fluentcore/model/implementation/CreatorTaskItem.java +++ b/azure-mgmt-resources/src/main/java/com/microsoft/azure/management/resources/fluentcore/model/implementation/CreatorTaskItem.java @@ -30,17 +30,13 @@ public ResourceT result() { @Override public Observable executeAsync() { - if (this.created == null) { - return this.resourceCreator.createResourceAsync() - .subscribeOn(Schedulers.io()) - .doOnNext(new Action1() { + return this.resourceCreator.createResourceAsync() + .subscribeOn(Schedulers.io()) + .doOnNext(new Action1() { @Override public void call(ResourceT resourceT) { created = resourceT; } }); - } else { - return Observable.just(created); - } } } diff --git a/runtimes/azure-client-runtime/src/main/java/com/microsoft/azure/TaskGroupBase.java b/runtimes/azure-client-runtime/src/main/java/com/microsoft/azure/TaskGroupBase.java index 747f0273fa7d..6de2a3ab4654 100644 --- a/runtimes/azure-client-runtime/src/main/java/com/microsoft/azure/TaskGroupBase.java +++ b/runtimes/azure-client-runtime/src/main/java/com/microsoft/azure/TaskGroupBase.java @@ -64,18 +64,31 @@ public Observable executeAsync() { final List> observables = new ArrayList<>(); while (nextNode != null) { final DAGNode thisNode = nextNode; - observables.add(nextNode.data().executeAsync() - .flatMap(new Func1>() { - @Override - public Observable call(T t) { - dag().reportedCompleted(thisNode); - if (dag().isRootNode(thisNode)) { - return Observable.just(t); - } else { + T cachedResult = nextNode.data().result(); + if (cachedResult != null && !this.dag().isRootNode(nextNode)) { + observables.add(Observable.just(cachedResult) + .flatMap(new Func1>() { + @Override + public Observable call(T t) { + dag().reportedCompleted(thisNode); return executeAsync(); } - } - })); + }) + ); + } else { + observables.add(nextNode.data().executeAsync() + .flatMap(new Func1>() { + @Override + public Observable call(T t) { + dag().reportedCompleted(thisNode); + if (dag().isRootNode(thisNode)) { + return Observable.just(t); + } else { + return executeAsync(); + } + } + })); + } nextNode = dag.getNext(); } return Observable.merge(observables); From cc0b48f3d451a51f91cc669708b61d0b4b0a871b Mon Sep 17 00:00:00 2001 From: anuchan Date: Sun, 4 Sep 2016 22:46:53 -0700 Subject: [PATCH 11/18] unit test for vmaccess extension --- ...irtualMachineExtensionOperationsTests.java | 53 ++++++++++++++++--- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java index ac2ac42d38cb..e5ac773b717e 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java @@ -9,10 +9,6 @@ import java.util.List; public class VirtualMachineExtensionOperationsTests extends ComputeManagementTestBase { - private static final String RG_NAME = ResourceNamer.randomResourceName("vmexttest", 15); - private static final String LOCATION = "eastus"; - private static final String VMNAME = "javavm"; - @BeforeClass public static void setup() throws Exception { createClients(); @@ -23,13 +19,56 @@ public static void cleanup() throws Exception { } @Test - public void canInstallUpdateUninstallExtension() throws Exception { + public void canResetPasswordUsingVMAccessExtension() throws Exception { + final String RG_NAME = ResourceNamer.randomResourceName("vmexttest", 15); + final String LOCATION = "eastus"; + final String VMNAME = "javavm"; + + // Create a Linux VM + // + VirtualMachine vm = computeManager.virtualMachines() + .define(VMNAME) + .withRegion(LOCATION) + .withNewResourceGroup(RG_NAME) + .withNewPrimaryNetwork("10.0.0.0/28") + .withPrimaryPrivateIpAddressDynamic() + .withoutPrimaryPublicIpAddress() + .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_14_04_LTS) + .withRootUserName("Foo12") + .withPassword("BaR@12abc!") + .withSize(VirtualMachineSizeTypes.STANDARD_D3) + .create(); + + // Using VMAccess Linux extension to reset the password for the existing user + // https://github.com/Azure/azure-linux-extensions/blob/master/VMAccess/README.md + // + vm.update() + .defineNewExtension("VMAccessForLinux") + .withPublisher("Microsoft.OSTCExtensions") + .withType("VMAccessForLinux") + .withVersion("1.4") + .withProtectedSetting("username", "Foo12") + .withProtectedSetting("password", "B12a6@12xyz!") + .withProtectedSetting("reset_ssh", "true") + .attach() + .apply(); + + Assert.assertTrue(vm.extensions().size() > 0); + Assert.assertTrue(vm.extensions().containsKey("VMAccessForLinux")); + } + + @Test + public void canInstallUninstallCustomExtension() throws Exception { + final String RG_NAME = ResourceNamer.randomResourceName("vmexttest", 15); + final String LOCATION = "eastus"; + final String VMNAME = "javavm"; + final String mySqlInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; final String installCommand = "bash install_mysql_server_5.6.sh Abc.123x("; List fileUris = new ArrayList<>(); fileUris.add(mySqlInstallScript); - // Create VM and add extension + // Create Linux VM with a custom extension to install MySQL // VirtualMachine vm = computeManager.virtualMachines() .define(VMNAME) @@ -59,7 +98,7 @@ public void canInstallUpdateUninstallExtension() throws Exception { Assert.assertEquals(customScriptExtension.typeName(), "CustomScriptForLinux"); Assert.assertEquals(customScriptExtension.autoUpgradeMinorVersionEnabled(), true); - // Remove the extension + // Remove the custom extension // vm.update() .withoutExtension("CustomScriptForLinux") From 5526a48b6efe42be029732bf786e49736ffa85c3 Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 6 Sep 2016 08:40:29 -0700 Subject: [PATCH 12/18] Enabling extension update as part of vm update --- .../compute/VirtualMachineExtension.java | 9 ++++++ .../ExternalChildResourcesImpl.java | 21 +++++++------ .../VirtualMachineExtensionImpl.java | 30 +++++++++++-------- .../VirtualMachineExtensionsImpl.java | 1 + ...irtualMachineExtensionOperationsTests.java | 12 +++++++- .../compute/childresource/PulletsImpl.java | 1 + 6 files changed, 50 insertions(+), 24 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java index ba7af954b132..785562020270 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtension.java @@ -479,6 +479,15 @@ interface WithSettings { */ Update withPublicSetting(String key, Object value); + /** + * Specifies a private settings entry. + * + * @param key the key of a private settings entry + * @param value the value of the private settings entry + * @return the next stage of the update + */ + Update withProtectedSetting(String key, Object value); + /** * Specifies public settings. * diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index 9068e14887e5..e8f7ef8bc2be 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -43,7 +43,6 @@ public abstract class ExternalChildResourcesImpl< protected ExternalChildResourcesImpl(ParentImplT parent, String childResourceName) { this.parent = parent; this.childResourceName = childResourceName; - this.initializeCollection(); } /** @@ -280,6 +279,16 @@ protected void addChildResource(FluentModelTImpl childResource) { this.collection.put(childResource.name(), childResource); } + /** + * Initializes the child resource collection. + */ + protected void initializeCollection() { + this.collection.clear(); + for (FluentModelTImpl childResource : this.listChildResources()) { + this.collection.put(childResource.name(), childResource); + } + } + /** * Gets the list of external child resources. * @@ -309,14 +318,4 @@ private FluentModelTImpl find(String name) { } return null; } - - /** - * Initializes the child resource collection. - */ - private void initializeCollection() { - this.collection.clear(); - for (FluentModelTImpl childResource : this.listChildResources()) { - this.collection.put(childResource.name(), childResource); - } - } } diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java index a2ca8022b0eb..4c2e00900a56 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionImpl.java @@ -35,18 +35,7 @@ class VirtualMachineExtensionImpl VirtualMachineExtensionsInner client) { super(name, parent, inner); this.client = client; - - if (this.inner().settings() == null) { - this.publicSettings = new LinkedHashMap<>(); - this.inner().withSettings(this.publicSettings); - } else { - this.publicSettings = (LinkedHashMap) this.inner().settings(); - } - - if (this.inner().protectedSettings() == null) { - this.protectedSettings = new LinkedHashMap<>(); - this.inner().withProtectedSettings(this.protectedSettings); - } + initializeSettings(); } protected static VirtualMachineExtensionImpl newVirtualMachineExtension(String name, @@ -230,6 +219,7 @@ public Observable createAsync() { @Override public VirtualMachineExtension call(ServiceResponse response) { self.setInner(response.getBody()); + self.initializeSettings(); return self; } }); @@ -262,4 +252,20 @@ private void nullifySettingsIfEmpty() { this.inner().withProtectedSettings(null); } } + + private void initializeSettings() { + if (this.inner().settings() == null) { + this.publicSettings = new LinkedHashMap<>(); + this.inner().withSettings(this.publicSettings); + } else { + this.publicSettings = (LinkedHashMap) this.inner().settings(); + } + + if (this.inner().protectedSettings() == null) { + this.protectedSettings = new LinkedHashMap<>(); + this.inner().withProtectedSettings(this.protectedSettings); + } else { + this.protectedSettings = (LinkedHashMap) this.inner().protectedSettings(); + } + } } \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java index 08c674430a97..42d8c0a7f7db 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineExtensionsImpl.java @@ -25,6 +25,7 @@ class VirtualMachineExtensionsImpl extends VirtualMachineExtensionsImpl(VirtualMachineExtensionsInner client, VirtualMachineImpl parent) { super(parent, "VirtualMachineExtension"); this.client = client; + this.initializeCollection(); } /** diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java index e5ac773b717e..914157c496db 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/VirtualMachineExtensionOperationsTests.java @@ -39,7 +39,7 @@ public void canResetPasswordUsingVMAccessExtension() throws Exception { .withSize(VirtualMachineSizeTypes.STANDARD_D3) .create(); - // Using VMAccess Linux extension to reset the password for the existing user + // Using VMAccess Linux extension to reset the password for the existing user 'Foo12' // https://github.com/Azure/azure-linux-extensions/blob/master/VMAccess/README.md // vm.update() @@ -55,6 +55,16 @@ public void canResetPasswordUsingVMAccessExtension() throws Exception { Assert.assertTrue(vm.extensions().size() > 0); Assert.assertTrue(vm.extensions().containsKey("VMAccessForLinux")); + + // Update the VMAccess Linux extension to reset password again for the user 'Foo12' + // + vm.update() + .updateExtension("VMAccessForLinux") + .withProtectedSetting("username", "Foo12") + .withProtectedSetting("password", "muy!234OR") + .withProtectedSetting("reset_ssh", "true") + .parent() + .apply(); } @Test diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java index f2bf678034f5..118263c3a064 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/PulletsImpl.java @@ -7,6 +7,7 @@ class PulletsImpl extends ExternalChildResourcesImpl { PulletsImpl(ChickenImpl parent) { super(parent, "Pullet"); + initializeCollection(); } public PulletImpl define(String name) { From fdf9d2b4918cd643b454f584add091b49b4c374f Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 6 Sep 2016 11:37:48 -0700 Subject: [PATCH 13/18] sample showing crud on virtual machine extensions --- .../management/compute/VirtualMachine.java | 3 +- .../implementation/VirtualMachineImpl.java | 2 +- .../ManageVirtualMachineExtension.java | 185 ++++++++++++++++++ .../azure/management/samples/Utils.java | 23 ++- 4 files changed, 206 insertions(+), 7 deletions(-) create mode 100644 azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java index f90298e92377..3f3e7e4d7c33 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachine.java @@ -193,9 +193,8 @@ public interface VirtualMachine extends /** * @return the extensions attached to the Azure Virtual Machine - * @throws Exception exceptions thrown from the cloud or from serialization/deserialization. */ - Map extensions() throws Exception; + Map extensions(); /** * @return the plan value diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java index 386c0d7dfec6..d0ea43de2ac9 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/VirtualMachineImpl.java @@ -826,7 +826,7 @@ public String licenseType() { } @Override - public Map extensions() throws Exception { + public Map extensions() { return this.virtualMachineExtensions.asMap(); } diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java new file mode 100644 index 000000000000..02235b414b28 --- /dev/null +++ b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java @@ -0,0 +1,185 @@ +package com.microsoft.azure.management.compute.samples; + +import com.microsoft.azure.Azure; +import com.microsoft.azure.management.compute.KnownLinuxVirtualMachineImage; +import com.microsoft.azure.management.compute.VirtualMachine; +import com.microsoft.azure.management.compute.VirtualMachineSizeTypes; +import com.microsoft.azure.management.resources.fluentcore.arm.Region; +import com.microsoft.azure.management.resources.fluentcore.utils.ResourceNamer; +import com.microsoft.azure.management.samples.Utils; +import okhttp3.logging.HttpLoggingInterceptor; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * Azure Compute sample for managing virtual machine extensions. - + * - Create a virtual machine with custom script extension to install MySQL + * - Reset ssh password of an existing user using VMAccess extension + * - Creates a new sudo user account with ssh password using VMAccess extension + * - Removes a sudo user account by using VMAccess extension + * - Removes the custom script extension + */ +public final class ManageVirtualMachineExtension { + /** + * Main entry point. + * @param args the parameters + */ + public static void main(String[] args) { + + final String linuxVmName = ResourceNamer.randomResourceName("lVM", 10); + final String rgName = ResourceNamer.randomResourceName("rgCOMV", 15); + final String pipDnsLabel = ResourceNamer.randomResourceName("rgPip", 25); + + final String firstUserName = "tirekicker"; + final String firstUserPassword = "12NewPA$$w0rd!"; + final String firstUserNewPassword = "muy!234OR"; + + final String secondUserName = "onemoreuser"; + final String secondUserPassword = "B12a6@12xyz!"; + final String secondUserExpiration = "2020-12-31"; + + final String customScriptExtensionName = "CustomScriptForLinux"; + final String customScriptExtensionPublisherName = "Microsoft.OSTCExtensions"; + final String customScriptExtensionTypeName = "CustomScriptForLinux"; + final String customScriptExtensionVersionName = "1.4"; + + final String mySqlInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; + final String scriptInstallCommand = "bash install_mysql_server_5.6.sh Abc.123x("; + final List fileUris = new ArrayList<>(); + fileUris.add(mySqlInstallScript); + + final String vmAccessExtensionName = "VMAccessForLinux"; + final String vmAccessExtensionPublisherName = "Microsoft.OSTCExtensions"; + final String vmAccessExtensionTypeName = "VMAccessForLinux"; + final String vmAccessExtensionVersionName = "1.4"; + + try { + + //============================================================= + // Authenticate + + final File credFile = new File(System.getenv("AZURE_AUTH_LOCATION")); + + Azure azure = Azure + .configure() + .withLogLevel(HttpLoggingInterceptor.Level.BASIC) + .authenticate(credFile) + .withDefaultSubscription(); + + // Print selected subscription + System.out.println("Selected subscription: " + azure.subscriptionId()); + + + try { + //============================================================= + // Create a Linux VM with root (sudo) user name and password + + System.out.println("Creating a Linux VM"); + + VirtualMachine vm = azure.virtualMachines().define(linuxVmName) + .withRegion(Region.US_EAST) + .withExistingResourceGroup(rgName) + .withNewPrimaryNetwork("10.0.0.0/28") + .withPrimaryPrivateIpAddressDynamic() + .withNewPrimaryPublicIpAddress(pipDnsLabel) + .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_14_04_LTS) + .withRootUserName(firstUserName) + .withPassword(firstUserPassword) + .withSize(VirtualMachineSizeTypes.STANDARD_D3_V2) + .defineNewExtension(customScriptExtensionName) + .withPublisher(customScriptExtensionPublisherName) + .withType(customScriptExtensionTypeName) + .withVersion(customScriptExtensionVersionName) + .withAutoUpgradeMinorVersionEnabled() + .withPublicSetting("fileUris", fileUris) + .withPublicSetting("commandToExecute", scriptInstallCommand) + .attach() + .create(); + + System.out.println("Created a Linux VM with MySQL" + vm.id()); + Utils.print(vm); + + //============================================================= + // Reset ssh password of an existing user using VMAccess extension + + vm.update() + .defineNewExtension(vmAccessExtensionName) + .withPublisher(vmAccessExtensionPublisherName) + .withType(vmAccessExtensionTypeName) + .withVersion(vmAccessExtensionVersionName) + .withProtectedSetting("username", firstUserName) + .withProtectedSetting("password", firstUserNewPassword) + .withProtectedSetting("reset_ssh", "true") + .attach() + .apply(); + + System.out.println("Added VMAccess extension and reset password of first user"); + Utils.print(vm); + + + //============================================================= + // Creates a new sudo user account with ssh password using VMAccess extension + + vm.update() + .defineNewExtension(vmAccessExtensionName) + .withPublisher(vmAccessExtensionPublisherName) + .withType(vmAccessExtensionTypeName) + .withVersion(vmAccessExtensionVersionName) + .withProtectedSetting("username", secondUserName) + .withProtectedSetting("password", secondUserPassword) + .withProtectedSetting("expiration", secondUserExpiration) + .attach() + .apply(); + + System.out.println("Added a new user to the virtual machine"); + Utils.print(vm); + + //============================================================= + // Removes the second sudo user account using VMAccess extension + + vm.update() + .defineNewExtension(vmAccessExtensionName) + .withPublisher(vmAccessExtensionPublisherName) + .withType(vmAccessExtensionTypeName) + .withVersion(vmAccessExtensionVersionName) + .withProtectedSetting("remove_user", secondUserName) + .attach() + .apply(); + + + //============================================================= + // Removes the custom script extension + + vm.update() + .withoutExtension(customScriptExtensionName) + .apply(); + System.out.println("Removed the custom script extension"); + Utils.print(vm); + } catch (Exception f) { + + System.out.println(f.getMessage()); + f.printStackTrace(); + + } finally { + try { + System.out.println("Deleting Resource Group: " + rgName); + azure.resourceGroups().delete(rgName); + System.out.println("Deleted Resource Group: " + rgName); + } catch (NullPointerException npe) { + System.out.println("Did not create any resources in Azure. No clean up is necessary"); + } catch (Exception g) { + g.printStackTrace(); + } + } + } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + } + } + + private ManageVirtualMachineExtension() { + + } +} diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/samples/Utils.java b/azure-samples/src/main/java/com/microsoft/azure/management/samples/Utils.java index 8ecf6daf320c..b1881647ca9b 100644 --- a/azure-samples/src/main/java/com/microsoft/azure/management/samples/Utils.java +++ b/azure-samples/src/main/java/com/microsoft/azure/management/samples/Utils.java @@ -11,6 +11,7 @@ import com.microsoft.azure.management.compute.AvailabilitySet; import com.microsoft.azure.management.compute.VirtualMachine; import com.microsoft.azure.management.compute.DataDisk; +import com.microsoft.azure.management.compute.VirtualMachineExtension; import com.microsoft.azure.management.network.Network; import com.microsoft.azure.management.network.NetworkInterface; import com.microsoft.azure.management.network.NetworkSecurityGroup; @@ -23,6 +24,7 @@ import java.io.IOException; import java.util.Calendar; import java.util.List; +import java.util.Map; import java.util.UUID; /** @@ -116,6 +118,21 @@ public static void print(VirtualMachine resource) { networkProfile.append("\n\t\tId:").append(networkInterfaceId); } + StringBuilder extensions = new StringBuilder().append("\n\tExtensions: "); + for (Map.Entry extensionEntry : resource.extensions().entrySet()) { + VirtualMachineExtension extension = extensionEntry.getValue(); + extensions.append("\n\t\tExtension: ").append(extension.id()) + .append("\n\t\t\tName: ").append(extension.name()) + .append("\n\t\t\tTags: ").append(extension.tags()) + .append("\n\t\t\tProvisioningState: ").append(extension.provisioningState()) + .append("\n\t\t\tAuto upgrade minor version enabled: ").append(extension.autoUpgradeMinorVersionEnabled()) + .append("\n\t\t\tPublisher: ").append(extension.publisherName()) + .append("\n\t\t\tType: ").append(extension.typeName()) + .append("\n\t\t\tVersion: ").append(extension.versionName()) + .append("\n\t\t\tPublic Settings: ").append(extension.publicSettingsAsJsonString()); + } + + System.out.println(new StringBuilder().append("Virtual Machine: ").append(resource.id()) .append("Name: ").append(resource.name()) .append("\n\tResource group: ").append(resource.resourceGroupName()) @@ -126,6 +143,7 @@ public static void print(VirtualMachine resource) { .append(storageProfile) .append(osProfile) .append(networkProfile) + .append(extensions) .toString()); } @@ -268,14 +286,11 @@ public static void print(StorageAccount storageAccount) { * @param storageAccountKeys a list of storage account keys */ public static void print(List storageAccountKeys) { - StorageAccountKey storageAccountKey; - for (int i = 0; i < storageAccountKeys.size(); i++) { - storageAccountKey = (StorageAccountKey) storageAccountKeys.get(i); + StorageAccountKey storageAccountKey = storageAccountKeys.get(i); System.out.println("Key (" + i + ") " + storageAccountKey.keyName() + "=" + storageAccountKey.value()); } - } /** From c79315340bf7bc1b912b627ce642419333d9de49 Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 6 Sep 2016 12:16:46 -0700 Subject: [PATCH 14/18] Adding sample for virtual machine extension images --- .../ListVirtualMachineExtensionImages.java | 94 +++++++++++++++++++ .../ManageVirtualMachineExtension.java | 20 ++-- 2 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ListVirtualMachineExtensionImages.java diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ListVirtualMachineExtensionImages.java b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ListVirtualMachineExtensionImages.java new file mode 100644 index 000000000000..5a4ab510fd6d --- /dev/null +++ b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ListVirtualMachineExtensionImages.java @@ -0,0 +1,94 @@ +package com.microsoft.azure.management.compute.samples; + +import com.microsoft.azure.Azure; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImage; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageType; +import com.microsoft.azure.management.compute.VirtualMachineExtensionImageVersion; +import com.microsoft.azure.management.compute.VirtualMachinePublisher; +import com.microsoft.azure.management.resources.fluentcore.arm.Region; +import okhttp3.logging.HttpLoggingInterceptor; + +import java.io.File; +import java.util.List; + +/** + * List all virtual machine extension image publishers and + * list all virtual machine extension images published by Microsoft.OSTCExtensions, Microsoft.Azure.Extensions + * by browsing through extension image publishers, types, and versions. + */ +public final class ListVirtualMachineExtensionImages { + /** + * The main entry point. + * + * @param args the parameters + */ + public static void main(String[] args) { + try { + + + //================================================================= + // Authenticate + + final File credFile = new File(System.getenv("AZURE_AUTH_LOCATION")); + + Azure azure = Azure + .configure() + .withLogLevel(HttpLoggingInterceptor.Level.NONE) + .authenticate(credFile) + .withDefaultSubscription(); + + //================================================================= + // List all virtual machine extension image publishers and + // list all virtual machine extension images + // published by Microsoft.OSTCExtensions and Microsoft.Azure.Extensions + // by browsing through extension image publishers, types, and versions + + List publishers = azure + .virtualMachineImages() + .publishers() + .listByRegion(Region.US_EAST); + + VirtualMachinePublisher chosenPublisher; + + System.out.println("US East data center: printing list of \n" + + "a) Publishers and\n" + + "b) virtual machine images published by Microsoft.OSTCExtensions and Microsoft.Azure.Extensions"); + System.out.println("======================================================="); + System.out.println("\n"); + + for (VirtualMachinePublisher publisher : publishers) { + + System.out.println("Publisher - " + publisher.name()); + + if (publisher.name().equalsIgnoreCase("Microsoft.OSTCExtensions") + | publisher.name().equalsIgnoreCase("Microsoft.Azure.Extensions")) { + + chosenPublisher = publisher; + System.out.print("\n\n"); + System.out.println("======================================================="); + System.out.println("Located " + chosenPublisher.name()); + System.out.println("======================================================="); + System.out.println("Printing entries as publisher/type/version"); + + for (VirtualMachineExtensionImageType imageType : chosenPublisher.extensionTypes().list()) { + for (VirtualMachineExtensionImageVersion version: imageType.versions().list()) { + VirtualMachineExtensionImage image = version.image(); + System.out.println("Image - " + chosenPublisher.name() + "/" + + image.typeName() + "/" + + image.versionName()); + } + } + + System.out.print("\n\n"); + + } + } + } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + } + } + + private ListVirtualMachineExtensionImages() { + } +} diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java index 02235b414b28..eb26a816d402 100644 --- a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java +++ b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java @@ -124,12 +124,12 @@ public static void main(String[] args) { vm.update() .defineNewExtension(vmAccessExtensionName) - .withPublisher(vmAccessExtensionPublisherName) - .withType(vmAccessExtensionTypeName) - .withVersion(vmAccessExtensionVersionName) - .withProtectedSetting("username", secondUserName) - .withProtectedSetting("password", secondUserPassword) - .withProtectedSetting("expiration", secondUserExpiration) + .withPublisher(vmAccessExtensionPublisherName) + .withType(vmAccessExtensionTypeName) + .withVersion(vmAccessExtensionVersionName) + .withProtectedSetting("username", secondUserName) + .withProtectedSetting("password", secondUserPassword) + .withProtectedSetting("expiration", secondUserExpiration) .attach() .apply(); @@ -141,10 +141,10 @@ public static void main(String[] args) { vm.update() .defineNewExtension(vmAccessExtensionName) - .withPublisher(vmAccessExtensionPublisherName) - .withType(vmAccessExtensionTypeName) - .withVersion(vmAccessExtensionVersionName) - .withProtectedSetting("remove_user", secondUserName) + .withPublisher(vmAccessExtensionPublisherName) + .withType(vmAccessExtensionTypeName) + .withVersion(vmAccessExtensionVersionName) + .withProtectedSetting("remove_user", secondUserName) .attach() .apply(); From 1c2b38521dd046486c8954d485ecbae1a008bca6 Mon Sep 17 00:00:00 2001 From: anuchan Date: Tue, 6 Sep 2016 20:22:19 -0700 Subject: [PATCH 15/18] Removing duplicate methods --- .../VirtualMachineExtensionImages.java | 27 ------------------- .../compute/VirtualMachineImages.java | 26 ------------------ 2 files changed, 53 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java index 9b62c9626960..a6a80ea663fb 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineExtensionImages.java @@ -1,12 +1,7 @@ package com.microsoft.azure.management.compute; -import com.microsoft.azure.CloudException; -import com.microsoft.azure.PagedList; -import com.microsoft.azure.management.resources.fluentcore.arm.Region; import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListingByRegion; -import java.io.IOException; - /** * Entry point to virtual machine extension image management API. */ @@ -15,26 +10,4 @@ public interface VirtualMachineExtensionImages extends SupportsListingByRegion - * Note this is a very long running call, as it enumerates through all publishers, types and versions. - * @return list of virtual machine extension images - * @param regionName the name of the region as used internally by Azure - * @throws CloudException exceptions thrown from the cloud - * @throws IOException exceptions thrown from serialization/deserialization - */ - PagedList listByRegion(String regionName) throws CloudException, IOException; - - /** - * Lists all the virtual machine extension images available in a given region. - *

- * Note this is a very long running call, as it enumerates through all publishers, types and versions. - * @return list of virtual machine extension images - * @param region the region to list the images from - * @throws CloudException exceptions thrown from the cloud - * @throws IOException exceptions thrown from serialization/deserialization - */ - PagedList listByRegion(Region region) throws CloudException, IOException; } \ No newline at end of file diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineImages.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineImages.java index 660065907b66..7f0fd764c314 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineImages.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/VirtualMachineImages.java @@ -5,10 +5,6 @@ */ package com.microsoft.azure.management.compute; -import java.io.IOException; -import com.microsoft.azure.CloudException; -import com.microsoft.azure.PagedList; -import com.microsoft.azure.management.resources.fluentcore.arm.Region; import com.microsoft.azure.management.resources.fluentcore.collection.SupportsListingByRegion; /** @@ -20,26 +16,4 @@ public interface VirtualMachineImages extends * @return entry point to virtual machine image publishers */ VirtualMachinePublishers publishers(); - - /** - * Lists all the virtual machine images available in a given region. - *

- * Note this is a very long running call, as it enumerates through all publishers, offers and skus. - * @return list of virtual machine images - * @param regionName the name of the region as used internally by Azure - * @throws CloudException exceptions thrown from the cloud - * @throws IOException exceptions thrown from serialization/deserialization - */ - PagedList listByRegion(String regionName) throws CloudException, IOException; - - /** - * Lists all the virtual machine images available in a given region. - *

- * Note this is a very long running call, as it enumerates through all publishers, offers and skus. - * @return list of virtual machine images - * @param region the region to list the images from - * @throws CloudException exceptions thrown from the cloud - * @throws IOException exceptions thrown from serialization/deserialization - */ - PagedList listByRegion(Region region) throws CloudException, IOException; } From 51af9ad80cb07947e0495f81774134baa0754fac Mon Sep 17 00:00:00 2001 From: anuchan Date: Wed, 7 Sep 2016 11:11:14 -0700 Subject: [PATCH 16/18] Updating sample to showcase updateExtension --- .../samples/ManageVirtualMachineExtension.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java index eb26a816d402..27bcab55d870 100644 --- a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java +++ b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java @@ -74,7 +74,7 @@ public static void main(String[] args) { try { //============================================================= - // Create a Linux VM with root (sudo) user name and password + // Create a Linux VM with root (sudo) user and install MySQL using custom script extension System.out.println("Creating a Linux VM"); @@ -123,14 +123,11 @@ public static void main(String[] args) { // Creates a new sudo user account with ssh password using VMAccess extension vm.update() - .defineNewExtension(vmAccessExtensionName) - .withPublisher(vmAccessExtensionPublisherName) - .withType(vmAccessExtensionTypeName) - .withVersion(vmAccessExtensionVersionName) + .updateExtension(vmAccessExtensionName) .withProtectedSetting("username", secondUserName) .withProtectedSetting("password", secondUserPassword) .withProtectedSetting("expiration", secondUserExpiration) - .attach() + .parent() .apply(); System.out.println("Added a new user to the virtual machine"); @@ -140,15 +137,11 @@ public static void main(String[] args) { // Removes the second sudo user account using VMAccess extension vm.update() - .defineNewExtension(vmAccessExtensionName) - .withPublisher(vmAccessExtensionPublisherName) - .withType(vmAccessExtensionTypeName) - .withVersion(vmAccessExtensionVersionName) + .updateExtension(vmAccessExtensionName) .withProtectedSetting("remove_user", secondUserName) - .attach() + .parent() .apply(); - //============================================================= // Removes the custom script extension From 123f6f0aadbed669ad61c708ee8d38f5988421c2 Mon Sep 17 00:00:00 2001 From: anuchan Date: Wed, 7 Sep 2016 21:57:50 -0700 Subject: [PATCH 17/18] updating vm extension sample scenarios --- .../ManageVirtualMachineExtension.java | 256 +++++++++++++----- 1 file changed, 187 insertions(+), 69 deletions(-) diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java index 27bcab55d870..14bdea327ecb 100644 --- a/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java +++ b/azure-samples/src/main/java/com/microsoft/azure/management/compute/samples/ManageVirtualMachineExtension.java @@ -2,6 +2,7 @@ import com.microsoft.azure.Azure; import com.microsoft.azure.management.compute.KnownLinuxVirtualMachineImage; +import com.microsoft.azure.management.compute.KnownWindowsVirtualMachineImage; import com.microsoft.azure.management.compute.VirtualMachine; import com.microsoft.azure.management.compute.VirtualMachineSizeTypes; import com.microsoft.azure.management.resources.fluentcore.arm.Region; @@ -15,11 +16,12 @@ /** * Azure Compute sample for managing virtual machine extensions. - - * - Create a virtual machine with custom script extension to install MySQL - * - Reset ssh password of an existing user using VMAccess extension - * - Creates a new sudo user account with ssh password using VMAccess extension - * - Removes a sudo user account by using VMAccess extension - * - Removes the custom script extension + * - Create a Linux and Windows virtual machine + * - Add three users (user names and passwords for windows, SSH keys for Linux) + * - Resets user credentials + * - Remove a user + * - Install MySQL on Linux | something significant on Windows + * - Remove extensions */ public final class ManageVirtualMachineExtension { /** @@ -29,31 +31,56 @@ public final class ManageVirtualMachineExtension { public static void main(String[] args) { final String linuxVmName = ResourceNamer.randomResourceName("lVM", 10); + final String windowsVmName = ResourceNamer.randomResourceName("wVM", 10); final String rgName = ResourceNamer.randomResourceName("rgCOMV", 15); - final String pipDnsLabel = ResourceNamer.randomResourceName("rgPip", 25); + final String pipDnsLabelLinuxVM = ResourceNamer.randomResourceName("rgPip1", 25); + final String pipDnsLabelWindowsVM = ResourceNamer.randomResourceName("rgPip2", 25); + + // Linux configurations + // + final String firstLinuxUserName = "tirekicker"; + final String firstLinuxUserPassword = "12NewPA$$w0rd!"; + final String firstLinuxUserNewPassword = "muy!234OR"; + + final String secondLinuxUserName = "seconduser"; + final String secondLinuxUserPassword = "B12a6@12xyz!"; + final String secondLinuxUserExpiration = "2020-12-31"; + + final String thirdLinuxUserName = "thirduser"; + final String thirdLinuxUserPassword = "12xyz!B12a6@"; + final String thirdLinuxUserExpiration = "2020-12-31"; + + final String linuxCustomScriptExtensionName = "CustomScriptForLinux"; + final String linuxCustomScriptExtensionPublisherName = "Microsoft.OSTCExtensions"; + final String linuxCustomScriptExtensionTypeName = "CustomScriptForLinux"; + final String linuxCustomScriptExtensionVersionName = "1.4"; + + final String mySqlLinuxInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; + final String mySqlScriptInstallCommand = "bash install_mysql_server_5.6.sh Abc.123x("; + final List fileUris = new ArrayList<>(); + fileUris.add(mySqlLinuxInstallScript); - final String firstUserName = "tirekicker"; - final String firstUserPassword = "12NewPA$$w0rd!"; - final String firstUserNewPassword = "muy!234OR"; + final String linuxVmAccessExtensionName = "VMAccessForLinux"; + final String linuxVmAccessExtensionPublisherName = "Microsoft.OSTCExtensions"; + final String linuxVmAccessExtensionTypeName = "VMAccessForLinux"; + final String linuxVmAccessExtensionVersionName = "1.4"; - final String secondUserName = "onemoreuser"; - final String secondUserPassword = "B12a6@12xyz!"; - final String secondUserExpiration = "2020-12-31"; + // Windows configurations + // + final String firstWindowsUserName = "tirekicker"; + final String firstWindowsUserPassword = "12NewPA$$w0rd!"; + final String firstWindowsUserNewPassword = "muy!234OR"; - final String customScriptExtensionName = "CustomScriptForLinux"; - final String customScriptExtensionPublisherName = "Microsoft.OSTCExtensions"; - final String customScriptExtensionTypeName = "CustomScriptForLinux"; - final String customScriptExtensionVersionName = "1.4"; + final String secondWindowsUserName = "seconduser"; + final String secondWindowsUserPassword = "B12a6@12xyz!"; - final String mySqlInstallScript = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/4397e808d07df60ff3cdfd1ae40999f0130eb1b3/mysql-standalone-server-ubuntu/scripts/install_mysql_server_5.6.sh"; - final String scriptInstallCommand = "bash install_mysql_server_5.6.sh Abc.123x("; - final List fileUris = new ArrayList<>(); - fileUris.add(mySqlInstallScript); + final String thirdWindowsUserName = "thirduser"; + final String thirdWindowsUserPassword = "12xyz!B12a6@"; - final String vmAccessExtensionName = "VMAccessForLinux"; - final String vmAccessExtensionPublisherName = "Microsoft.OSTCExtensions"; - final String vmAccessExtensionTypeName = "VMAccessForLinux"; - final String vmAccessExtensionVersionName = "1.4"; + final String windowsVmAccessExtensionName = "VMAccessAgent"; + final String windowsVmAccessExtensionPublisherName = "Microsoft.Compute"; + final String windowsVmAccessExtensionTypeName = "VMAccessAgent"; + final String windowsVmAccessExtensionVersionName = "2.3"; try { @@ -73,83 +100,174 @@ public static void main(String[] args) { try { + + //============================================================= - // Create a Linux VM with root (sudo) user and install MySQL using custom script extension + // Create a Linux VM with root (sudo) user System.out.println("Creating a Linux VM"); - VirtualMachine vm = azure.virtualMachines().define(linuxVmName) + VirtualMachine linuxVM = azure.virtualMachines().define(linuxVmName) .withRegion(Region.US_EAST) - .withExistingResourceGroup(rgName) + .withNewResourceGroup(rgName) .withNewPrimaryNetwork("10.0.0.0/28") .withPrimaryPrivateIpAddressDynamic() - .withNewPrimaryPublicIpAddress(pipDnsLabel) + .withNewPrimaryPublicIpAddress(pipDnsLabelLinuxVM) .withPopularLinuxImage(KnownLinuxVirtualMachineImage.UBUNTU_SERVER_14_04_LTS) - .withRootUserName(firstUserName) - .withPassword(firstUserPassword) + .withRootUserName(firstLinuxUserName) + .withPassword(firstLinuxUserPassword) .withSize(VirtualMachineSizeTypes.STANDARD_D3_V2) - .defineNewExtension(customScriptExtensionName) - .withPublisher(customScriptExtensionPublisherName) - .withType(customScriptExtensionTypeName) - .withVersion(customScriptExtensionVersionName) + .create(); + + System.out.println("Created a Linux VM with" + linuxVM.id()); + Utils.print(linuxVM); + + //============================================================= + // Add a second sudo user to Linux VM using VMAccess extension + + linuxVM.update() + .defineNewExtension(linuxVmAccessExtensionName) + .withPublisher(linuxVmAccessExtensionPublisherName) + .withType(linuxVmAccessExtensionTypeName) + .withVersion(linuxVmAccessExtensionVersionName) + .withProtectedSetting("username", secondLinuxUserName) + .withProtectedSetting("password", secondLinuxUserPassword) + .withProtectedSetting("expiration", secondLinuxUserExpiration) + .attach() + .apply(); + + System.out.println("Added a second sudo user to the Linux VM"); + + //============================================================= + // Add a third sudo user to Linux VM by updating VMAccess extension + + linuxVM.update() + .updateExtension(linuxVmAccessExtensionName) + .withProtectedSetting("username", thirdLinuxUserName) + .withProtectedSetting("password", thirdLinuxUserPassword) + .withProtectedSetting("expiration", thirdLinuxUserExpiration) + .parent() + .apply(); + + System.out.println("Added a third sudo user to the Linux VM"); + + //============================================================= + // Reset ssh password of first user of Linux VM by updating VMAccess extension + + linuxVM.update() + .updateExtension(linuxVmAccessExtensionName) + .withProtectedSetting("username", firstLinuxUserName) + .withProtectedSetting("password", firstLinuxUserNewPassword) + .withProtectedSetting("reset_ssh", "true") + .parent() + .apply(); + + System.out.println("Password of first user of Linux VM has been updated"); + + //============================================================= + // Removes the second sudo user from Linux VM using VMAccess extension + + linuxVM.update() + .updateExtension(linuxVmAccessExtensionName) + .withProtectedSetting("remove_user", secondLinuxUserName) + .parent() + .apply(); + + //============================================================= + // Install MySQL in Linux VM using CustomScript extension + + linuxVM.update() + .defineNewExtension(linuxCustomScriptExtensionName) + .withPublisher(linuxCustomScriptExtensionPublisherName) + .withType(linuxCustomScriptExtensionTypeName) + .withVersion(linuxCustomScriptExtensionVersionName) .withAutoUpgradeMinorVersionEnabled() .withPublicSetting("fileUris", fileUris) - .withPublicSetting("commandToExecute", scriptInstallCommand) + .withPublicSetting("commandToExecute", mySqlScriptInstallCommand) .attach() + .apply(); + + System.out.println("Installed MySql using custom script extension"); + Utils.print(linuxVM); + + //============================================================= + // Removes the extensions from Linux VM + + linuxVM.update() + .withoutExtension(linuxCustomScriptExtensionName) + .withoutExtension(linuxVmAccessExtensionName) + .apply(); + System.out.println("Removed the custom script and VM Access extensions from Linux VM"); + Utils.print(linuxVM); + + //============================================================= + // Create a Windows VM with admin user + + System.out.println("Creating a Windows VM"); + + VirtualMachine windowsVM = azure.virtualMachines().define(windowsVmName) + .withRegion(Region.US_EAST) + .withExistingResourceGroup(rgName) + .withNewPrimaryNetwork("10.0.0.0/28") + .withPrimaryPrivateIpAddressDynamic() + .withNewPrimaryPublicIpAddress(pipDnsLabelWindowsVM) + .withPopularWindowsImage(KnownWindowsVirtualMachineImage.WINDOWS_SERVER_2012_R2_DATACENTER) + .withAdminUserName(firstWindowsUserName) + .withPassword(firstWindowsUserPassword) + .withSize(VirtualMachineSizeTypes.STANDARD_D3_V2) .create(); - System.out.println("Created a Linux VM with MySQL" + vm.id()); - Utils.print(vm); + System.out.println("Created a Windows VM" + windowsVM.id()); + Utils.print(windowsVM); //============================================================= - // Reset ssh password of an existing user using VMAccess extension - - vm.update() - .defineNewExtension(vmAccessExtensionName) - .withPublisher(vmAccessExtensionPublisherName) - .withType(vmAccessExtensionTypeName) - .withVersion(vmAccessExtensionVersionName) - .withProtectedSetting("username", firstUserName) - .withProtectedSetting("password", firstUserNewPassword) - .withProtectedSetting("reset_ssh", "true") + // Add a second admin user to Windows VM using VMAccess extension + + windowsVM.update() + .defineNewExtension(windowsVmAccessExtensionName) + .withPublisher(windowsVmAccessExtensionPublisherName) + .withType(windowsVmAccessExtensionTypeName) + .withVersion(windowsVmAccessExtensionVersionName) + .withProtectedSetting("username", secondWindowsUserName) + .withProtectedSetting("password", secondWindowsUserPassword) .attach() .apply(); - System.out.println("Added VMAccess extension and reset password of first user"); - Utils.print(vm); - + System.out.println("Added a second admin user to the Windows VM"); //============================================================= - // Creates a new sudo user account with ssh password using VMAccess extension - - vm.update() - .updateExtension(vmAccessExtensionName) - .withProtectedSetting("username", secondUserName) - .withProtectedSetting("password", secondUserPassword) - .withProtectedSetting("expiration", secondUserExpiration) - .parent() + // Add a third admin user to Windows VM by updating VMAccess extension + + windowsVM.update() + .updateExtension(windowsVmAccessExtensionName) + .withProtectedSetting("username", thirdWindowsUserName) + .withProtectedSetting("password", thirdWindowsUserPassword) + .parent() .apply(); - System.out.println("Added a new user to the virtual machine"); - Utils.print(vm); + System.out.println("Added a third admin user to the Windows VM"); //============================================================= - // Removes the second sudo user account using VMAccess extension + // Reset admin password of first user of Windows VM by updating VMAccess extension - vm.update() - .updateExtension(vmAccessExtensionName) - .withProtectedSetting("remove_user", secondUserName) + windowsVM.update() + .updateExtension(windowsVmAccessExtensionName) + .withProtectedSetting("username", firstWindowsUserName) + .withProtectedSetting("password", firstWindowsUserNewPassword) .parent() .apply(); + System.out.println("Password of first user of Windows VM has been updated"); + //============================================================= - // Removes the custom script extension + // Removes the extensions from Linux VM - vm.update() - .withoutExtension(customScriptExtensionName) + windowsVM.update() + .withoutExtension(windowsVmAccessExtensionName) .apply(); - System.out.println("Removed the custom script extension"); - Utils.print(vm); + System.out.println("Removed the VM Access extensions from Windows VM"); + Utils.print(windowsVM); + } catch (Exception f) { System.out.println(f.getMessage()); From 2cea55cf2ae361c25ccc68a1a7a2e250e1426984 Mon Sep 17 00:00:00 2001 From: anuchan Date: Thu, 8 Sep 2016 10:05:17 -0700 Subject: [PATCH 18/18] Some comments update and minor changes --- .../ExternalChildResourceImpl.java | 33 ++++++++++---- .../ExternalChildResourcesImpl.java | 44 ++++++++++++------- .../ExternalChildResourceTests.java | 19 ++++++-- 3 files changed, 67 insertions(+), 29 deletions(-) diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java index bc3fad75ddfc..e9386b2174f2 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourceImpl.java @@ -6,10 +6,17 @@ /** * Externalized child resource abstract implementation. + * Inorder to be eligible for an external child resource following criteria must be satisfied: + * 1. It's is always associated with a parent resource and has no existence without parent + * i.e. if you delete parent then child resource will be deleted automatically. + * 2. Parent will contain collection of child resources. this is not a hard requirement. + * 3. It's has an ID and can be created, updated, fetched and deleted independent of the parent + * i.e. CRUD on child resource does not require CRUD on the parent * (Internal use only) + * * @param the fluent model type of the child resource - * @param Azure inner resource class type representing this child resource - * @param the parent Azure resource class type of this child resource + * @param Azure inner resource class type representing the child resource + * @param the parent Azure resource class type of the child resource */ public abstract class ExternalChildResourceImpl< FluentModelT extends ExternalChildResource, @@ -17,12 +24,21 @@ public abstract class ExternalChildResourceImpl< ParentImplT> extends IndexableRefreshableWrapperImpl { + /** + * State representing any pending action that needs to be performed on this child resource. + */ private State state = State.None; + /** + * The child resource name. + */ private final String name; + /** + * Reference to the parent of the child resource. + */ protected final ParentImplT parent; /** - * Creates an external child resource. + * Creates an instance of external child resource in-memory. * * @param name the name of this external child resource * @param parent reference to the parent of this external child resource @@ -42,7 +58,8 @@ public String name() { } /** - * @return the in-memory state of this child resource + * @return the in-memory state of this child resource and state represents any pending action on the + * child resource. */ public State state() { return this.state; @@ -79,7 +96,7 @@ public void setState(State newState) { public abstract Observable deleteAsync(); /** - * The possible state of an child resource in-memory. + * The possible states of a child resource in-memory. */ public enum State { /** @@ -87,15 +104,15 @@ public enum State { */ None, /** - * Resource required to be created. + * Child resource required to be created. */ ToBeCreated, /** - * Resource required to be updated. + * Child resource required to be updated. */ ToBeUpdated, /** - * Resource required to be deleted. + * Child resource required to be deleted. */ ToBeRemoved } diff --git a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java index e8f7ef8bc2be..4db79da7e8f5 100644 --- a/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java +++ b/azure-mgmt-compute/src/main/java/com/microsoft/azure/management/compute/implementation/ExternalChildResourcesImpl.java @@ -19,19 +19,29 @@ /** * Externalized child resource collection abstract implementation. + * (Internal use only) * * @param the implementation of {@param FluentModelT} - * @param the type of the external child resource - * @param the type of the external child resource inner - * @param the type of parent of the external child resources + * @param the fluent model type of the child resource + * @param Azure inner resource class type representing the child resource + * @param the parent Azure resource class type of the child resources */ public abstract class ExternalChildResourcesImpl< FluentModelTImpl extends ExternalChildResourceImpl, FluentModelT extends ExternalChildResource, InnerModelT, ParentImplT> { + /** + * The parent resource of this collection of child resources. + */ private final ParentImplT parent; + /** + * Used to construct error string, this is user friendly name of the child resource (e.g. Subnet, Extension). + */ private final String childResourceName; + /** + * The child resource instances that this collection contains. + */ private ConcurrentMap collection = new ConcurrentHashMap<>(); /** @@ -46,7 +56,7 @@ protected ExternalChildResourcesImpl(ParentImplT parent, String childResourceNam } /** - * Refresh the collection from the parent. + * Refresh the collection. */ public void refresh() { initializeCollection(); @@ -65,8 +75,8 @@ public void refresh() { public Observable commitAsync() { final ExternalChildResourcesImpl self = this; List items = new ArrayList<>(); - for (FluentModelTImpl extension : this.collection.values()) { - items.add(extension); + for (FluentModelTImpl item : this.collection.values()) { + items.add(item); } final List exceptionsList = Collections.synchronizedList(new ArrayList()); @@ -216,16 +226,16 @@ protected ParentImplT parent() { } /** - * @return the collection + * @return the collection of external child resources. */ protected Map collection() { return this.collection; } /** - * Prepare for definition of a new child resource. + * Prepare for definition of a new external child resource. * - * @param name the name for the new child resource + * @param name the name for the new external child resource * @return the child resource */ protected FluentModelTImpl prepareDefine(String name) { @@ -238,10 +248,10 @@ protected FluentModelTImpl prepareDefine(String name) { } /** - * Prepare for a child resource update. + * Prepare for an external child resource update. * - * @param name the name of the child resource - * @return the child resource + * @param name the name of the external child resource + * @return the external child resource to be updated */ protected FluentModelTImpl prepareUpdate(String name) { FluentModelTImpl childResource = find(name); @@ -257,9 +267,9 @@ protected FluentModelTImpl prepareUpdate(String name) { } /** - * Mark the child resource with given name as to be removed. + * Mark an external child resource with given name as to be removed. * - * @param name the name of the child resource + * @param name the name of the external child resource */ protected void prepareRemove(String name) { FluentModelTImpl childResource = find(name); @@ -271,16 +281,16 @@ protected void prepareRemove(String name) { } /** - * Adds a child resource to the collection. + * Adds an external child resource to the collection. * - * @param childResource the child resource + * @param childResource the external child resource */ protected void addChildResource(FluentModelTImpl childResource) { this.collection.put(childResource.name(), childResource); } /** - * Initializes the child resource collection. + * Initializes the external child resource collection. */ protected void initializeCollection() { this.collection.clear(); diff --git a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java index cbab39618b7a..92eb380f0d09 100644 --- a/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java +++ b/azure-mgmt-compute/src/test/java/com/microsoft/azure/management/compute/childresource/ExternalChildResourceTests.java @@ -14,9 +14,19 @@ public class ExternalChildResourceTests { @Test public void noCommitIfNoChange() throws InterruptedException { - ChickenImpl chicken = new ChickenImpl(); - PulletsImpl pullets = chicken.pullets(); + ChickenImpl chicken = new ChickenImpl(); // Parent resource + PulletsImpl pullets = chicken.pullets(); // Child resource collection final CountDownLatch monitor = new CountDownLatch(1); + // Note that commitAsync() won't be exposed to the end-user as it's a part of child resource impl + // pullets.commitAsync will be called from (Applicable)chicken.applyAsync() or (Creatable)chicken.createAsync(). + // + // Observable Chicken::ApplyAsync() { + // [1] update chicken + // [2] update pullets by calling pullets.commitAsync() + // } + // + // In the unit test cases we call it directly as we testing external child resource here. + // pullets.commitAsync() .subscribe(new Subscriber() { @Override @@ -40,7 +50,7 @@ public void onNext(PulletImpl pullet) { @Test public void shouldCommitCreateUpdateAndDelete() throws InterruptedException { - ChickenImpl chicken = new ChickenImpl(); + ChickenImpl chicken = new ChickenImpl(); // Parent resource chicken .defineNewPullet("alice") .withAge(1) @@ -52,6 +62,7 @@ public void shouldCommitCreateUpdateAndDelete() throws InterruptedException { final List changedPuppets = new ArrayList<>(); final CountDownLatch monitor = new CountDownLatch(1); + PulletsImpl pullets = chicken.pullets(); pullets.commitAsync() .subscribe(new Observer() { @@ -80,7 +91,7 @@ public void onNext(PulletImpl pullet) { @Test public void shouldEmitErrorAfterAllSuccessfulCommit() throws InterruptedException { - ChickenImpl chicken = new ChickenImpl(); + ChickenImpl chicken = new ChickenImpl(); // Parent resource chicken .defineNewPullet("alice") .withAge(1)