From d0956b7c4754b919cadefb691b5bdd0b38ab5dbd Mon Sep 17 00:00:00 2001 From: Jianghao Lu Date: Wed, 23 Nov 2016 14:33:31 -0800 Subject: [PATCH 1/3] Web app cannot be created in a different region than its plan --- .../management/website/AppServicePlans.java | 9 + .../azure/management/website/WebApp.java | 94 +++- .../azure/management/website/WebAppBase.java | 56 --- .../implementation/AppServicePlansImpl.java | 13 +- .../implementation/WebAppBaseImpl.java | 444 +++++++++--------- .../website/implementation/WebAppImpl.java | 41 +- .../management/website/WebAppsTests.java | 3 +- 7 files changed, 366 insertions(+), 294 deletions(-) diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/AppServicePlans.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/AppServicePlans.java index 05c9fef68f2c8..1c3722877d968 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/AppServicePlans.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/AppServicePlans.java @@ -12,6 +12,7 @@ import com.microsoft.azure.management.resources.fluentcore.arm.collection.SupportsListingByGroup; import com.microsoft.azure.management.resources.fluentcore.collection.SupportsCreating; import com.microsoft.azure.management.resources.fluentcore.collection.SupportsDeletingById; +import rx.Observable; /** * Entry point for app service plan management API. @@ -23,4 +24,12 @@ public interface AppServicePlans extends SupportsGettingByGroup, SupportsGettingById, SupportsDeletingByGroup { + /** + * Gets the information about a resource from Azure based on the resource name and the name of its resource group. + * + * @param resourceGroupName the name of the resource group the resource is in + * @param name the name of the resource. (Note, this is not the ID) + * @return an immutable representation of the resource + */ + Observable getByGroupAsync(String resourceGroupName, String name); } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebApp.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebApp.java index bc980c1e0e688..b989c0c72384b 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebApp.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebApp.java @@ -29,30 +29,112 @@ public interface WebApp extends */ interface Definition extends DefinitionStages.Blank, - DefinitionStages.WithGroup { + DefinitionStages.WithRegion, + DefinitionStages.WithAppServicePlan, + DefinitionStages.WithNewAppServicePlan { } /** * Grouping of all the web app definition stages. */ interface DefinitionStages { + /** + * A web app definition allowing resource group to be set. + */ + interface Blank extends GroupableResource.DefinitionStages.WithGroup { + } + /** * The first stage of the web app definition. */ - interface Blank extends GroupableResource.DefinitionWithRegion { + interface WithRegion extends GroupableResource.DefinitionWithRegion { } /** - * A web app definition allowing resource group to be set. + * A web app definition allowing app service plan to be set. */ - interface WithGroup extends GroupableResource.DefinitionStages.WithGroup< - WebAppBase.DefinitionStages.WithAppServicePlan> { + interface WithAppServicePlan { + /** + * Creates a new app service plan to use. + * @return the next stage of the web app definition + * @param name the name of the app service plan + */ + WithRegion withNewAppServicePlan(String name); + + /** + * Uses an existing app service plan for the web app. + * @param appServicePlanName the name of the existing app service plan + * @return the next stage of the web app definition + */ + WebAppBase.DefinitionStages.WithHostNameBinding withExistingAppServicePlan(String appServicePlanName); + } + + /** + * As web app definition allowing more information of a new app service plan to be set. + */ + interface WithNewAppServicePlan { + /** + * Creates a new free app service plan to use. No custom domains or SSL bindings are available in this plan. + * @return the next stage of the web app definition + */ + WebAppBase.DefinitionStages.WithCreate withFreePricingTier(); + + /** + * Creates a new app service plan to use. + * @param pricingTier the pricing tier to use + * @return the next stage of the web app definition + */ + WebAppBase.DefinitionStages.WithHostNameBinding withPricingTier(AppServicePricingTier pricingTier); + } + } + + /** + * Grouping of all the web app update stages. + */ + interface UpdateStages { + /** + * A web app update allowing app service plan to be set. + */ + interface WithAppServicePlan { + /** + * Creates a new app service plan to use. + * @return the next stage of the web app update + * @param name the name of the app service plan + */ + WithNewAppServicePlan withNewAppServicePlan(String name); + + /** + * Uses an existing app service plan for the web app. + * @param appServicePlanName the name of the existing app service plan + * @return the next stage of the web app update + */ + Update withExistingAppServicePlan(String appServicePlanName); + } + + /** + * As web app update allowing more information of a new app service plan to be set. + */ + interface WithNewAppServicePlan { + /** + * Creates a new free app service plan to use. No custom domains or SSL bindings are available in this plan. + * @return the next stage of the web app update + */ + Update withFreePricingTier(); + + /** + * Creates a new app service plan to use. + * @param pricingTier the pricing tier to use + * @return the next stage of the web app update + */ + Update withPricingTier(AppServicePricingTier pricingTier); } } /** * The template for a web app update operation, containing all the settings that can be modified. */ - interface Update extends WebAppBase.Update { + interface Update extends + WebAppBase.Update, + UpdateStages.WithAppServicePlan { } } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java index 0a5e478327a18..864b9661c6bd3 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java @@ -277,7 +277,6 @@ public interface WebAppBase> extends * Container interface for all the definitions that need to be implemented. */ interface Definition extends - DefinitionStages.WithAppServicePlan, DefinitionStages.WithHostNameSslBinding, DefinitionStages.WithWebContainer { } @@ -286,33 +285,6 @@ interface Definition extends * Grouping of all the site definition stages. */ interface DefinitionStages { - /** - * A web app definition allowing app service plan to be set. - * @param the type of the resource, either a web app or a deployment slot - */ - interface WithAppServicePlan { - /** - * Creates a new free app service plan to use. No custom domains or SSL bindings are available in this plan. - * @return the next stage of the web app definition - */ - WithCreate withNewFreeAppServicePlan(); - - /** - * Creates a new app service plan to use. - * @param name the name of the app service plan - * @param pricingTier the pricing tier to use - * @return the next stage of the web app definition - */ - WithHostNameBinding withNewAppServicePlan(String name, AppServicePricingTier pricingTier); - - /** - * Uses an existing app service plan for the web app. - * @param appServicePlanName the name of the existing app service plan - * @return the next stage of the web app definition - */ - WithHostNameBinding withExistingAppServicePlan(String appServicePlanName); - } - /** * A web app definition stage allowing host name binding to be specified. * @param the type of the resource, either a web app or a deployment slot @@ -609,33 +581,6 @@ interface WithCreate extends * Grouping of all the web app update stages. */ interface UpdateStages { - /** - * The stage of the web app update allowing app service plan to be set. - * @param the type of the resource, either a web app or a deployment slot - */ - interface WithAppServicePlan { - /** - * Creates a new free app service plan to use. No custom domains or SSL bindings are available in this plan. - * @return the next stage of web app update - */ - Update withNewFreeAppServicePlan(); - - /** - * Creates a new app service plan to use. - * @param name the name of the app service plan - * @param pricingTier the pricing tier to use - * @return the next stage of web app update - */ - Update withNewAppServicePlan(String name, AppServicePricingTier pricingTier); - - /** - * Uses an existing app service plan for the web app. - * @param appServicePlanName the name of the existing app service plan - * @return the next stage of web app update - */ - Update withExistingAppServicePlan(String appServicePlanName); - } - /** * The stage of the web app update allowing host name binding to be set. * @param the type of the resource, either a web app or a deployment slot @@ -953,7 +898,6 @@ interface WithConnectionString { */ interface Update extends Appliable, - UpdateStages.WithAppServicePlan, UpdateStages.WithHostNameBinding, UpdateStages.WithHostNameSslBinding, UpdateStages.WithClientAffinityEnabled, diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/AppServicePlansImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/AppServicePlansImpl.java index 9814c51d577ac..fec026e6bdf7b 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/AppServicePlansImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/AppServicePlansImpl.java @@ -31,7 +31,7 @@ class AppServicePlansImpl @Override public AppServicePlan getByGroup(String groupName, String name) { - return wrapModel(innerCollection.get(groupName, name)); + return getByGroupAsync(groupName, name).toBlocking().single(); } @Override @@ -67,4 +67,15 @@ protected AppServicePlanImpl wrapModel(AppServicePlanInner inner) { public AppServicePlanImpl define(String name) { return wrapModel(name); } + + @Override + public Observable getByGroupAsync(String resourceGroupName, String name) { + return innerCollection.getAsync(resourceGroupName, name) + .map(new Func1() { + @Override + public AppServicePlan call(AppServicePlanInner appServicePlanInner) { + return wrapModel(appServicePlanInner); + } + }); + } } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java index f862a9e786058..e3caa7a253056 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java @@ -10,12 +10,10 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.microsoft.azure.management.resources.fluentcore.arm.models.implementation.GroupableResourceImpl; -import com.microsoft.azure.management.resources.fluentcore.utils.ResourceNamer; import com.microsoft.azure.management.resources.fluentcore.utils.Utils; import com.microsoft.azure.management.website.AppServiceCertificate; import com.microsoft.azure.management.website.AppServiceDomain; import com.microsoft.azure.management.website.AppServicePlan; -import com.microsoft.azure.management.website.AppServicePricingTier; import com.microsoft.azure.management.website.AppSetting; import com.microsoft.azure.management.website.AzureResourceType; import com.microsoft.azure.management.website.CloningInfo; @@ -40,6 +38,7 @@ import com.microsoft.azure.management.website.WebContainer; import org.joda.time.DateTime; import rx.Observable; +import rx.functions.Action1; import rx.functions.Func1; import rx.functions.FuncN; @@ -418,246 +417,235 @@ public Observable createResourceAsync() { if (hostNameSslStateMap.size() > 0) { inner().withHostNameSslStates(new ArrayList<>(hostNameSslStateMap.values())); } - if (inner().siteConfig() != null & inner().siteConfig().location() == null) { - inner().siteConfig().withLocation(inner().location()); + Observable locationObs = Observable.just(inner().location()); + if (inner().location() == null) { + locationObs = myManager.appServicePlans().getByGroupAsync(resourceGroupName(), inner().serverFarmId()) + .map(new Func1() { + @Override + public String call(AppServicePlan appServicePlan) { + return appServicePlan.regionName(); + } + }); } + locationObs = locationObs.doOnNext(new Action1() { + @Override + public void call(String s) { + inner().withLocation(s); + if (inner().siteConfig() != null && inner().siteConfig().location() == null) { + inner().siteConfig().withLocation(s); + } + } + }); // Construct web app observable - return createOrUpdateInner(inner()) - // Submit hostname bindings - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner site) { - List> bindingObservables = new ArrayList<>(); - for (HostNameBindingImpl binding: hostNameBindingsToCreate.values()) { - bindingObservables.add(binding.createAsync()); + return locationObs.flatMap(new Func1>() { + @Override + public Observable call(String s) { + return createOrUpdateInner(inner()); + } + }) + // Submit hostname bindings + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner site) { + List> bindingObservables = new ArrayList<>(); + for (HostNameBindingImpl binding: hostNameBindingsToCreate.values()) { + bindingObservables.add(binding.createAsync()); + } + for (String binding: hostNameBindingsToDelete) { + bindingObservables.add(deleteHostNameBinding(binding).map(new Func1() { + @Override + public HostNameBinding call(Object o) { + return null; + } + })); + } + if (bindingObservables.isEmpty()) { + return Observable.just(site); + } else { + return Observable.zip(bindingObservables, new FuncN() { + @Override + public SiteInner call(Object... args) { + return site; } - for (String binding: hostNameBindingsToDelete) { - bindingObservables.add(deleteHostNameBinding(binding).map(new Func1() { + }); + } + } + }) + // refresh after hostname bindings + .flatMap(new Func1>() { + @Override + public Observable call(SiteInner site) { + return getInner(); + } + }) + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner siteInner) { + List> certs = new ArrayList<>(); + for (final HostNameSslBindingImpl binding : sslBindingsToCreate.values()) { + certs.add(binding.newCertificate()); + siteInner.hostNameSslStates().add(binding.inner().withToUpdate(true)); + } + if (certs.isEmpty()) { + return Observable.just(siteInner); + } else { + return Observable.zip(certs, new FuncN() { + @Override + public SiteInner call(Object... args) { + return siteInner; + } + }); + } + } + }) + // refresh after hostname SSL bindings + .flatMap(new Func1>() { + @Override + public Observable call(SiteInner site) { + return createOrUpdateInner(inner()); + } + }) + // submit config + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner siteInner) { + if (inner().siteConfig() == null) { + return Observable.just(siteInner); + } + return createOrUpdateSiteConfig(inner().siteConfig()) + .flatMap(new Func1>() { + @Override + public Observable call(SiteConfigInner siteConfigInner) { + siteInner.withSiteConfig(siteConfigInner); + return Observable.just(siteInner); + } + }); + } + }) + // app settings + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner inner) { + Observable observable = Observable.just(inner); + if (!appSettingsToAdd.isEmpty() || !appSettingsToRemove.isEmpty()) { + observable = listAppSettings() + .flatMap(new Func1>() { @Override - public HostNameBinding call(Object o) { - return null; + public Observable call(StringDictionaryInner stringDictionaryInner) { + if (stringDictionaryInner == null) { + stringDictionaryInner = new StringDictionaryInner(); + stringDictionaryInner.withLocation(regionName()); + } + if (stringDictionaryInner.properties() == null) { + stringDictionaryInner.withProperties(new HashMap()); + } + stringDictionaryInner.properties().putAll(appSettingsToAdd); + for (String appSettingKey : appSettingsToRemove) { + stringDictionaryInner.properties().remove(appSettingKey); + } + return updateAppSettings(stringDictionaryInner); } - })); - } - if (bindingObservables.isEmpty()) { - return Observable.just(site); - } else { - return Observable.zip(bindingObservables, new FuncN() { + }).map(new Func1() { @Override - public SiteInner call(Object... args) { - return site; + public SiteInner call(StringDictionaryInner stringDictionaryInner) { + return inner; } }); - } - } - }) - // refresh after hostname bindings - .flatMap(new Func1>() { - @Override - public Observable call(SiteInner site) { - return getInner(); - } - }) - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner siteInner) { - List> certs = new ArrayList<>(); - for (final HostNameSslBindingImpl binding : sslBindingsToCreate.values()) { - certs.add(binding.newCertificate()); - siteInner.hostNameSslStates().add(binding.inner().withToUpdate(true)); - } - if (certs.isEmpty()) { - return Observable.just(siteInner); - } else { - return Observable.zip(certs, new FuncN() { + } + return observable; + } + }) + // connection strings + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner inner) { + Observable observable = Observable.just(inner); + if (!connectionStringsToAdd.isEmpty() || !connectionStringsToRemove.isEmpty()) { + observable = listConnectionStrings() + .flatMap(new Func1>() { + @Override + public Observable call(ConnectionStringDictionaryInner dictionaryInner) { + if (dictionaryInner == null) { + dictionaryInner = new ConnectionStringDictionaryInner(); + dictionaryInner.withLocation(regionName()); + } + if (dictionaryInner.properties() == null) { + dictionaryInner.withProperties(new HashMap()); + } + dictionaryInner.properties().putAll(connectionStringsToAdd); + for (String connectionString : connectionStringsToRemove) { + dictionaryInner.properties().remove(connectionString); + } + return updateConnectionStrings(dictionaryInner); + } + }).map(new Func1() { @Override - public SiteInner call(Object... args) { - return siteInner; + public SiteInner call(ConnectionStringDictionaryInner stringDictionaryInner) { + return inner; } }); - } - } - }) - // refresh after hostname SSL bindings - .flatMap(new Func1>() { - @Override - public Observable call(SiteInner site) { - return createOrUpdateInner(inner()); - } - }) - // submit config - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner siteInner) { - if (inner().siteConfig() == null) { - return Observable.just(siteInner); - } - return createOrUpdateSiteConfig(inner().siteConfig()) - .flatMap(new Func1>() { - @Override - public Observable call(SiteConfigInner siteConfigInner) { - siteInner.withSiteConfig(siteConfigInner); - return Observable.just(siteInner); + } + return observable; + } + }) + // app setting & connection string stickiness + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner inner) { + Observable observable = Observable.just(inner); + if (!appSettingStickiness.isEmpty() || !connectionStringStickiness.isEmpty()) { + observable = listSlotConfigurations() + .flatMap(new Func1>() { + @Override + public Observable call(SlotConfigNamesResourceInner slotConfigNamesResourceInner) { + if (slotConfigNamesResourceInner == null) { + slotConfigNamesResourceInner = new SlotConfigNamesResourceInner(); + slotConfigNamesResourceInner.withLocation(regionName()); } - }); - } - }) - // app settings - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner inner) { - Observable observable = Observable.just(inner); - if (!appSettingsToAdd.isEmpty() || !appSettingsToRemove.isEmpty()) { - observable = listAppSettings() - .flatMap(new Func1>() { - @Override - public Observable call(StringDictionaryInner stringDictionaryInner) { - if (stringDictionaryInner == null) { - stringDictionaryInner = new StringDictionaryInner(); - stringDictionaryInner.withLocation(regionName()); - } - if (stringDictionaryInner.properties() == null) { - stringDictionaryInner.withProperties(new HashMap()); - } - stringDictionaryInner.properties().putAll(appSettingsToAdd); - for (String appSettingKey : appSettingsToRemove) { - stringDictionaryInner.properties().remove(appSettingKey); - } - return updateAppSettings(stringDictionaryInner); - } - }).map(new Func1() { - @Override - public SiteInner call(StringDictionaryInner stringDictionaryInner) { - return inner; - } - }); - } - return observable; - } - }) - // connection strings - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner inner) { - Observable observable = Observable.just(inner); - if (!connectionStringsToAdd.isEmpty() || !connectionStringsToRemove.isEmpty()) { - observable = listConnectionStrings() - .flatMap(new Func1>() { - @Override - public Observable call(ConnectionStringDictionaryInner dictionaryInner) { - if (dictionaryInner == null) { - dictionaryInner = new ConnectionStringDictionaryInner(); - dictionaryInner.withLocation(regionName()); - } - if (dictionaryInner.properties() == null) { - dictionaryInner.withProperties(new HashMap()); - } - dictionaryInner.properties().putAll(connectionStringsToAdd); - for (String connectionString : connectionStringsToRemove) { - dictionaryInner.properties().remove(connectionString); - } - return updateConnectionStrings(dictionaryInner); - } - }).map(new Func1() { - @Override - public SiteInner call(ConnectionStringDictionaryInner stringDictionaryInner) { - return inner; - } - }); - } - return observable; - } - }) - // app setting & connection string stickiness - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner inner) { - Observable observable = Observable.just(inner); - if (!appSettingStickiness.isEmpty() || !connectionStringStickiness.isEmpty()) { - observable = listSlotConfigurations() - .flatMap(new Func1>() { - @Override - public Observable call(SlotConfigNamesResourceInner slotConfigNamesResourceInner) { - if (slotConfigNamesResourceInner == null) { - slotConfigNamesResourceInner = new SlotConfigNamesResourceInner(); - slotConfigNamesResourceInner.withLocation(regionName()); - } - if (slotConfigNamesResourceInner.appSettingNames() == null) { - slotConfigNamesResourceInner.withAppSettingNames(new ArrayList()); - } - if (slotConfigNamesResourceInner.connectionStringNames() == null) { - slotConfigNamesResourceInner.withConnectionStringNames(new ArrayList()); - } - Set stickyAppSettingKeys = new HashSet<>(slotConfigNamesResourceInner.appSettingNames()); - Set stickyConnectionStringNames = new HashSet<>(slotConfigNamesResourceInner.connectionStringNames()); - for (Map.Entry stickiness : appSettingStickiness.entrySet()) { - if (stickiness.getValue()) { - stickyAppSettingKeys.add(stickiness.getKey()); - } else { - stickyAppSettingKeys.remove(stickiness.getKey()); - } - } - for (Map.Entry stickiness : connectionStringStickiness.entrySet()) { - if (stickiness.getValue()) { - stickyConnectionStringNames.add(stickiness.getKey()); - } else { - stickyConnectionStringNames.remove(stickiness.getKey()); - } - } - slotConfigNamesResourceInner.withAppSettingNames(new ArrayList<>(stickyAppSettingKeys)); - slotConfigNamesResourceInner.withConnectionStringNames(new ArrayList<>(stickyConnectionStringNames)); - return updateSlotConfigurations(slotConfigNamesResourceInner); + if (slotConfigNamesResourceInner.appSettingNames() == null) { + slotConfigNamesResourceInner.withAppSettingNames(new ArrayList()); + } + if (slotConfigNamesResourceInner.connectionStringNames() == null) { + slotConfigNamesResourceInner.withConnectionStringNames(new ArrayList()); + } + Set stickyAppSettingKeys = new HashSet<>(slotConfigNamesResourceInner.appSettingNames()); + Set stickyConnectionStringNames = new HashSet<>(slotConfigNamesResourceInner.connectionStringNames()); + for (Map.Entry stickiness : appSettingStickiness.entrySet()) { + if (stickiness.getValue()) { + stickyAppSettingKeys.add(stickiness.getKey()); + } else { + stickyAppSettingKeys.remove(stickiness.getKey()); } - }).map(new Func1() { - @Override - public SiteInner call(SlotConfigNamesResourceInner slotConfigNamesResourceInner) { - return inner; + } + for (Map.Entry stickiness : connectionStringStickiness.entrySet()) { + if (stickiness.getValue()) { + stickyConnectionStringNames.add(stickiness.getKey()); + } else { + stickyConnectionStringNames.remove(stickiness.getKey()); } - }); - } - return observable; - } - }) - // convert from inner - .map(new Func1() { - @Override - public FluentT call(SiteInner siteInner) { - setInner(siteInner); - return normalizeProperties(); - } - }); - } - - @Override - @SuppressWarnings("unchecked") - public FluentImplT withNewFreeAppServicePlan() { - String appServicePlanName = ResourceNamer.randomResourceName(name(), 10); - AppServicePlan.DefinitionStages.WithCreate creatable = myManager.appServicePlans().define(appServicePlanName) - .withRegion(region()) - .withNewResourceGroup(resourceGroupName()) - .withPricingTier(AppServicePricingTier.FREE_F1); - addCreatableDependency(creatable); - inner().withServerFarmId(appServicePlanName); - return (FluentImplT) this; - } - - @Override - @SuppressWarnings("unchecked") - public FluentImplT withNewAppServicePlan(String name, AppServicePricingTier pricingTier) { - AppServicePlan.DefinitionStages.WithCreate creatable = myManager.appServicePlans().define(name) - .withRegion(region()) - .withNewResourceGroup(resourceGroupName()) - .withPricingTier(pricingTier); - addCreatableDependency(creatable); - inner().withServerFarmId(name); - return (FluentImplT) this; - } - - @Override - @SuppressWarnings("unchecked") - public FluentImplT withExistingAppServicePlan(String appServicePlanName) { - inner().withServerFarmId(appServicePlanName); - return (FluentImplT) this; + } + slotConfigNamesResourceInner.withAppSettingNames(new ArrayList<>(stickyAppSettingKeys)); + slotConfigNamesResourceInner.withConnectionStringNames(new ArrayList<>(stickyConnectionStringNames)); + return updateSlotConfigurations(slotConfigNamesResourceInner); + } + }).map(new Func1() { + @Override + public SiteInner call(SlotConfigNamesResourceInner slotConfigNamesResourceInner) { + return inner; + } + }); + } + return observable; + } + }) + // convert from inner + .map(new Func1() { + @Override + public FluentT call(SiteInner siteInner) { + setInner(siteInner); + return normalizeProperties(); + } + }); } WebAppBaseImpl withNewHostNameSslBinding(final HostNameSslBindingImpl hostNameSslBinding) { diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java index bc11cf61b5570..34bf14688760a 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java @@ -8,6 +8,9 @@ import com.google.common.base.Function; import com.google.common.collect.Maps; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; +import com.microsoft.azure.management.resources.implementation.ResourceGroupInner; +import com.microsoft.azure.management.website.AppServicePricingTier; import com.microsoft.azure.management.website.DeploymentSlots; import com.microsoft.azure.management.website.HostNameBinding; import com.microsoft.azure.management.website.WebApp; @@ -26,9 +29,11 @@ class WebAppImpl implements WebApp, WebApp.Definition, - WebApp.Update { + WebApp.Update, + WebApp.UpdateStages.WithNewAppServicePlan { private DeploymentSlots deploymentSlots; + private AppServicePlanImpl appServicePlan; WebAppImpl(String name, SiteInner innerObject, SiteConfigInner configObject, final WebAppsInner client, AppServiceManager manager) { super(name, innerObject, configObject, client, manager); @@ -142,4 +147,38 @@ public WebAppImpl refresh() { this.setInner(client.get(resourceGroupName(), name())); return this; } + + @Override + public WebAppImpl withNewAppServicePlan(String name) { + appServicePlan = (AppServicePlanImpl) myManager.appServicePlans().define(name); + inner().withServerFarmId(name); + return this; + } + + @Override + public WebAppImpl withFreePricingTier() { + return withPricingTier(AppServicePricingTier.FREE_F1); + } + + @Override + @SuppressWarnings("unchecked") + public WebAppImpl withPricingTier(AppServicePricingTier pricingTier) { + appServicePlan = appServicePlan + .withRegion(region()) + .withPricingTier(pricingTier); + if (super.creatableGroup != null) { + appServicePlan = appServicePlan.withNewResourceGroup(resourceGroupName()); + ((Wrapper) super.creatableGroup).inner().withLocation(regionName()); + } else { + appServicePlan = appServicePlan.withExistingResourceGroup(resourceGroupName()); + } + addCreatableDependency(appServicePlan); + return this; + } + + @Override + public WebAppImpl withExistingAppServicePlan(String appServicePlanName) { + inner().withServerFarmId(appServicePlanName); + return this; + } } \ No newline at end of file diff --git a/azure-mgmt-website/src/test/java/com/microsoft/azure/management/website/WebAppsTests.java b/azure-mgmt-website/src/test/java/com/microsoft/azure/management/website/WebAppsTests.java index 4a31e2fd613b8..9433ead39a9bf 100644 --- a/azure-mgmt-website/src/test/java/com/microsoft/azure/management/website/WebAppsTests.java +++ b/azure-mgmt-website/src/test/java/com/microsoft/azure/management/website/WebAppsTests.java @@ -7,7 +7,6 @@ package com.microsoft.azure.management.website; import com.microsoft.azure.management.resources.ResourceGroup; -import com.microsoft.azure.management.resources.fluentcore.arm.Region; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -31,8 +30,8 @@ public static void cleanup() throws Exception { @Test public void canCRUDWebApp() throws Exception { AppServiceDomain domain = appServiceManager.domains().getByGroup(RG_NAME, "graph-webapp-319.com"); + WebApp webApp = appServiceManager.webApps().define(WEBAPP_NAME) - .withRegion(Region.US_WEST) .withExistingResourceGroup(RG_NAME) .withExistingAppServicePlan("java-plan-323") .withManagedHostnameBindings(domain, "pineapple") From 4a74dee194ba8364e03da93d72e96a321087d176 Mon Sep 17 00:00:00 2001 From: Jianghao Lu Date: Mon, 28 Nov 2016 11:41:25 -0800 Subject: [PATCH 2/3] Add a simple app service sample --- azure-mgmt-website/pom.xml | 1 - .../website/implementation/DomainsInner.java | 1 + .../implementation/WebAppBaseImpl.java | 63 +++--- azure-samples/pom.xml | 5 + .../azure/management/samples/Utils.java | 122 +++++++++-- .../website/samples/ManageAppService.java | 192 ++++++++++++++++++ .../website/samples/package-info.java | 8 + azure/pom.xml | 5 + .../com/microsoft/azure/management/Azure.java | 18 ++ 9 files changed, 367 insertions(+), 48 deletions(-) create mode 100644 azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java create mode 100644 azure-samples/src/main/java/com/microsoft/azure/management/website/samples/package-info.java diff --git a/azure-mgmt-website/pom.xml b/azure-mgmt-website/pom.xml index 7f6e35b46e656..afa64b489887c 100644 --- a/azure-mgmt-website/pom.xml +++ b/azure-mgmt-website/pom.xml @@ -14,7 +14,6 @@ azure-mgmt-website jar - 1.0.0-SNAPSHOT Microsoft Azure SDK for Website Management This package contains Microsoft Azure Website Management SDK. diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DomainsInner.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DomainsInner.java index 004629f81b619..705a255975ef6 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DomainsInner.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DomainsInner.java @@ -1132,6 +1132,7 @@ public Observable> call(Response response) private ServiceResponse deleteDelegate(Response response) throws CloudException, IOException, IllegalArgumentException { return new AzureServiceResponseBuilder(this.client.mapperAdapter()) + .register(200, new TypeToken() { }.getType()) .register(204, new TypeToken() { }.getType()) .registerError(CloudException.class) .build(response); diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java index e3caa7a253056..695fc7bccf81d 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java @@ -38,7 +38,6 @@ import com.microsoft.azure.management.website.WebContainer; import org.joda.time.DateTime; import rx.Observable; -import rx.functions.Action1; import rx.functions.Func1; import rx.functions.FuncN; @@ -372,7 +371,8 @@ public Map getAppSettings() { return Maps.asMap(inner.properties().keySet(), new Function() { @Override public AppSetting apply(String input) { - return new AppSettingImpl(input, inner.properties().get(input), slotConfigs.appSettingNames().contains(input)); + return new AppSettingImpl(input, inner.properties().get(input), + slotConfigs.appSettingNames() != null && slotConfigs.appSettingNames().contains(input)); } }); } @@ -423,19 +423,11 @@ public Observable createResourceAsync() { .map(new Func1() { @Override public String call(AppServicePlan appServicePlan) { + inner().withLocation(appServicePlan.regionName()); return appServicePlan.regionName(); } }); } - locationObs = locationObs.doOnNext(new Action1() { - @Override - public void call(String s) { - inner().withLocation(s); - if (inner().siteConfig() != null && inner().siteConfig().location() == null) { - inner().siteConfig().withLocation(s); - } - } - }); // Construct web app observable return locationObs.flatMap(new Func1>() { @Override @@ -443,6 +435,25 @@ public Observable call(String s) { return createOrUpdateInner(inner()); } }) + // submit config + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner siteInner) { + if (inner().siteConfig() == null) { + return Observable.just(siteInner); + } else if (inner().siteConfig().location() == null) { + inner().siteConfig().withLocation(inner().location()); + } + return createOrUpdateSiteConfig(inner().siteConfig()) + .flatMap(new Func1>() { + @Override + public Observable call(SiteConfigInner siteConfigInner) { + siteInner.withSiteConfig(siteConfigInner); + return Observable.just(siteInner); + } + }); + } + }) // Submit hostname bindings .flatMap(new Func1>() { @Override @@ -478,6 +489,7 @@ public Observable call(SiteInner site) { return getInner(); } }) + // Submit SSL bindings .flatMap(new Func1>() { @Override public Observable call(final SiteInner siteInner) { @@ -494,34 +506,15 @@ public Observable call(final SiteInner siteInner) { public SiteInner call(Object... args) { return siteInner; } + }).flatMap(new Func1>() { + @Override + public Observable call(SiteInner inner) { + return createOrUpdateInner(inner); + } }); } } }) - // refresh after hostname SSL bindings - .flatMap(new Func1>() { - @Override - public Observable call(SiteInner site) { - return createOrUpdateInner(inner()); - } - }) - // submit config - .flatMap(new Func1>() { - @Override - public Observable call(final SiteInner siteInner) { - if (inner().siteConfig() == null) { - return Observable.just(siteInner); - } - return createOrUpdateSiteConfig(inner().siteConfig()) - .flatMap(new Func1>() { - @Override - public Observable call(SiteConfigInner siteConfigInner) { - siteInner.withSiteConfig(siteConfigInner); - return Observable.just(siteInner); - } - }); - } - }) // app settings .flatMap(new Func1>() { @Override diff --git a/azure-samples/pom.xml b/azure-samples/pom.xml index a9871d5e81cc5..5a995eabe7816 100644 --- a/azure-samples/pom.xml +++ b/azure-samples/pom.xml @@ -74,6 +74,11 @@ azure-mgmt-compute 1.0.0-beta4-SNAPSHOT + + com.microsoft.azure + azure-mgmt-website + 1.0.0-beta4-SNAPSHOT + com.microsoft.azure api-annotations 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 5b9b0b151ce2b..3560da3fc7e3c 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 @@ -19,36 +19,46 @@ import com.microsoft.azure.management.compute.VirtualMachineExtension; import com.microsoft.azure.management.keyvault.AccessPolicy; import com.microsoft.azure.management.keyvault.Vault; +import com.microsoft.azure.management.network.LoadBalancer; +import com.microsoft.azure.management.network.LoadBalancerBackend; +import com.microsoft.azure.management.network.LoadBalancerFrontend; +import com.microsoft.azure.management.network.LoadBalancerHttpProbe; +import com.microsoft.azure.management.network.LoadBalancerInboundNatPool; +import com.microsoft.azure.management.network.LoadBalancerInboundNatRule; +import com.microsoft.azure.management.network.LoadBalancerPrivateFrontend; +import com.microsoft.azure.management.network.LoadBalancerProbe; +import com.microsoft.azure.management.network.LoadBalancerPublicFrontend; +import com.microsoft.azure.management.network.LoadBalancerTcpProbe; +import com.microsoft.azure.management.network.LoadBalancingRule; import com.microsoft.azure.management.network.Network; import com.microsoft.azure.management.network.NetworkInterface; import com.microsoft.azure.management.network.NetworkSecurityGroup; import com.microsoft.azure.management.network.NetworkSecurityRule; import com.microsoft.azure.management.network.PublicIpAddress; import com.microsoft.azure.management.network.Subnet; -import com.microsoft.azure.management.network.LoadBalancer; -import com.microsoft.azure.management.network.LoadBalancerTcpProbe; -import com.microsoft.azure.management.network.LoadBalancingRule; -import com.microsoft.azure.management.network.LoadBalancerInboundNatPool; -import com.microsoft.azure.management.network.LoadBalancerInboundNatRule; -import com.microsoft.azure.management.network.LoadBalancerFrontend; -import com.microsoft.azure.management.network.LoadBalancerBackend; -import com.microsoft.azure.management.network.LoadBalancerProbe; -import com.microsoft.azure.management.network.LoadBalancerHttpProbe; -import com.microsoft.azure.management.network.LoadBalancerPublicFrontend; -import com.microsoft.azure.management.network.LoadBalancerPrivateFrontend; import com.microsoft.azure.management.redis.RedisAccessKeys; import com.microsoft.azure.management.redis.RedisCache; import com.microsoft.azure.management.redis.RedisCachePremium; import com.microsoft.azure.management.redis.ScheduleEntry; import com.microsoft.azure.management.storage.StorageAccount; import com.microsoft.azure.management.storage.StorageAccountKey; +import com.microsoft.azure.management.website.AppServiceCertificateOrder; +import com.microsoft.azure.management.website.AppServiceDomain; +import com.microsoft.azure.management.website.AppServicePlan; +import com.microsoft.azure.management.website.AppSetting; +import com.microsoft.azure.management.website.ConnectionString; +import com.microsoft.azure.management.website.Contact; +import com.microsoft.azure.management.website.HostNameBinding; +import com.microsoft.azure.management.website.HostNameSslState; +import com.microsoft.azure.management.website.SslState; +import com.microsoft.azure.management.website.WebAppBase; import java.io.IOException; +import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.ArrayList; import java.util.UUID; /** @@ -637,6 +647,94 @@ public static void print(BatchAccount batchAccount) { .toString()); } + /** + * Print app service domain. + * @param resource an app service domain + */ + public static void print(AppServiceDomain resource) { + StringBuilder builder = new StringBuilder().append("Domain: ").append(resource.id()) + .append("Name: ").append(resource.name()) + .append("\n\tResource group: ").append(resource.resourceGroupName()) + .append("\n\tRegion: ").append(resource.region()) + .append("\n\tCreated time: ").append(resource.createdTime()) + .append("\n\tExpiration time: ").append(resource.expirationTime()) + .append("\n\tContact: "); + Contact contact = resource.registrantContact(); + if (contact == null) { + builder = builder.append("Private"); + } else { + builder = builder.append("\n\t\tName: ").append(contact.nameFirst() + " " + contact.nameLast()); + } + builder = builder.append("\n\tName servers: "); + for (String nameServer: resource.nameServers()) { + builder = builder.append("\n\t\t" + nameServer); + } + System.out.println(builder.toString()); + } + + /** + * Print app service certificate order. + * @param resource an app service certificate order + */ + public static void print(AppServiceCertificateOrder resource) { + StringBuilder builder = new StringBuilder().append("App service certificate order: ").append(resource.id()) + .append("Name: ").append(resource.name()) + .append("\n\tResource group: ").append(resource.resourceGroupName()) + .append("\n\tRegion: ").append(resource.region()) + .append("\n\tDistinguished name: ").append(resource.distinguishedName()) + .append("\n\tProduct type: ").append(resource.productType()) + .append("\n\tValid years: ").append(resource.validityInYears()) + .append("\n\tStatus: ").append(resource.status()) + .append("\n\tIssuance time: ").append(resource.lastCertificateIssuanceTime()) + .append("\n\tSigned certificate: ").append(resource.signedCertificate() == null ? null : resource.signedCertificate().thumbprint()); + System.out.println(builder.toString()); + } + + /** + * Print app service plan. + * @param resource an app service plan + */ + public static void print(AppServicePlan resource) { + StringBuilder builder = new StringBuilder().append("App service certificate order: ").append(resource.id()) + .append("Name: ").append(resource.name()) + .append("\n\tResource group: ").append(resource.resourceGroupName()) + .append("\n\tRegion: ").append(resource.region()) + .append("\n\tPricing tier: ").append(resource.pricingTier()); + System.out.println(builder.toString()); + } + + /** + * Print a web app. + * @param resource a web app + */ + public static void print(WebAppBase resource) { + StringBuilder builder = new StringBuilder().append("Web app: ").append(resource.id()) + .append("Name: ").append(resource.name()) + .append("\n\tResource group: ").append(resource.resourceGroupName()) + .append("\n\tRegion: ").append(resource.region()) + .append("\n\tDefault hostname: ").append(resource.defaultHostName()) + .append("\n\tApp service plan: ").append(resource.appServicePlanId()) + .append("\n\tHost name bindings: "); + for (HostNameBinding binding: resource.getHostNameBindings().values()) { + builder = builder.append("\n\t\t" + binding.toString()); + } + builder = builder.append("\n\tSSL bindings: "); + for (HostNameSslState binding: resource.hostNameSslStates().values()) { + builder = builder.append("\n\t\t" + binding.name() + ": " + binding.sslState()); + if (binding.sslState() != null && binding.sslState() != SslState.DISABLED) { + builder = builder.append(" - " + binding.thumbprint()); + } + } + builder = builder.append("\n\tApp settings: "); + for (AppSetting setting: resource.getAppSettings().values()) { + builder = builder.append("\n\t\t" + setting.key() + ": " + setting.value() + (setting.sticky() ? " - slot setting" : "")); + } + builder = builder.append("\n\tConnection strings: "); + for (ConnectionString conn: resource.getConnectionStrings().values()) { + builder = builder.append("\n\t\t" + conn.name() + ": " + conn.value() + " - " + conn.type() + (conn.sticky() ? " - slot setting" : "")); + } + System.out.println(builder.toString()); + } /** * Creates and returns a randomized name based on the prefix file for use by the sample. diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java new file mode 100644 index 0000000000000..4af585ef106eb --- /dev/null +++ b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java @@ -0,0 +1,192 @@ +/** + * + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + * + */ + +package com.microsoft.azure.management.website.samples; + +import com.microsoft.azure.management.Azure; +import com.microsoft.azure.management.resources.fluentcore.arm.CountryISOCode; +import com.microsoft.azure.management.resources.fluentcore.arm.CountryPhoneCode; +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 com.microsoft.azure.management.website.AppServiceDomain; +import com.microsoft.azure.management.website.AppServicePricingTier; +import com.microsoft.azure.management.website.CertificateProductType; +import com.microsoft.azure.management.website.CustomHostNameDnsRecordType; +import com.microsoft.azure.management.website.WebApp; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.logging.HttpLoggingInterceptor; + +import java.io.File; +import java.io.IOException; + +/** + * Azure App Service sample for managing web apps - + * - Create 2 web apps under the same new app service plan + * - Authorize an application + * - Update a key vault + * - alter configurations + * - change permissions + * - Create another key vault + * - List key vaults + * - Delete a key vault. + */ +public final class ManageAppService { + + private static OkHttpClient httpClient; + + /** + * Main entry point. + * @param args the parameters + */ + public static void main(String[] args) { + final String app1Name = ResourceNamer.randomResourceName("webapp1", 20); + final String app2Name = ResourceNamer.randomResourceName("webapp2", 20); + final String planName = ResourceNamer.randomResourceName("jplan", 15); + final String domainName = ResourceNamer.randomResourceName("jsdk", 10) + ".com"; + final String certName = ResourceNamer.randomResourceName("democrt", 20); + final String vaultName = ResourceNamer.randomResourceName("demovault", 20); + final String rgName = ResourceNamer.randomResourceName("rgNEMV", 24); + + try { + + //============================================================= + // Authenticate + + final File credFile = new File(System.getenv("AZURE_AUTH_LOCATION")); + + Azure azure = Azure + .configure() + .withLogLevel(HttpLoggingInterceptor.Level.BODY) + .authenticate(credFile) + .withDefaultSubscription(); + + // Print selected subscription + System.out.println("Selected subscription: " + azure.subscriptionId()); + try { + + + //============================================================ + // Create a web app with a new app service plan + + System.out.println("Creating web app " + app1Name + "..."); + + WebApp app1 = azure.webApps() + .define(app1Name) + .withNewResourceGroup(rgName) + .withNewAppServicePlan(planName) + .withRegion(Region.US_WEST) + .withPricingTier(AppServicePricingTier.STANDARD_S1) + .create(); + + System.out.println("Created web app " + app1.name()); + Utils.print(app1); + + //============================================================ + // Create a second web app with the same app service plan + + System.out.println("Creating another web app " + app2Name + "..."); + + WebApp app2 = azure.webApps() + .define(app2Name) + .withExistingResourceGroup(rgName) + .withExistingAppServicePlan(planName) + .create(); + + System.out.println("Created web app " + app2.name()); + Utils.print(app2); + + //============================================================ + // Purchase a domain (will be canceled for a full refund) + + System.out.println("Purchasing a domain " + domainName + "..."); + + AppServiceDomain domain = azure.appServices().domains() + .define(domainName) + .withExistingResourceGroup(rgName) + .defineRegistrantContact() + .withFirstName("Microsoft") + .withLastName("Azure") + .withEmail("azure@outlook.com") + .withAddressLine1("1 Microsoft Way") + .withCity("Redmond") + .withStateOrProvince("Washington") + .withCountry(CountryISOCode.UNITED_STATES) + .withPostalCode("98052") + .withPhoneCountryCode(CountryPhoneCode.UNITED_STATES) + .withPhoneNumber("4258828080") + .attach() + .create(); + + System.out.println("Purchased domain " + domain.name()); + Utils.print(domain); + + //============================================================ + // Bind domain to web app 1 + + System.out.println("Binding https://app1." + domainName + " to web app " + app1Name + "..."); + + app1.update() + .defineHostnameBinding() + .withAzureManagedDomain(domain) + .withSubDomain("app1") + .withDnsRecordType(CustomHostNameDnsRecordType.CNAME) + .attach() + .defineSslBinding() + .forHostname("app1." + domainName) + .withNewAppServiceCertificateOrder(certName, CertificateProductType.STANDARD_DOMAIN_VALIDATED_SSL) + .withNewKeyVault(vaultName) + .withSniBasedSsl() + .attach() + .apply(); + + System.out.println("Finish binding https://app1." + domainName + " to web app " + app1Name + "..."); + Utils.print(app1); + + System.out.println("CURLing https://app1." + domainName); + System.out.println(curl("https://app1." + domainName)); + + //============================================================ + // Bind domain to web app 2 and also purchase a certificate + + System.out.println("Binding www." + domainName + " to web app " + app1Name + "..."); + + } catch (Exception e) { + System.err.println(e.getMessage()); + e.printStackTrace(); + } finally { + try { + System.out.println("Deleting Resource Group: " + rgName); + azure.resourceGroups().deleteByName(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 static String curl(String url) { + Request request = new Request.Builder().url(url).get().build(); + try { + return httpClient.newCall(request).execute().body().string(); + } catch (IOException e) { + return null; + } + } + + private ManageAppService() { + httpClient = new OkHttpClient.Builder().build(); + } +} diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/package-info.java b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/package-info.java new file mode 100644 index 0000000000000..22a373de8ca9b --- /dev/null +++ b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/package-info.java @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +/** + * This package contains the classes for azure app service samples. + */ +package com.microsoft.azure.management.website.samples; diff --git a/azure/pom.xml b/azure/pom.xml index af0a463750acf..3a6ec941e05ea 100644 --- a/azure/pom.xml +++ b/azure/pom.xml @@ -104,6 +104,11 @@ azure-mgmt-redis 1.0.0-beta4-SNAPSHOT + + com.microsoft.azure + azure-mgmt-website + 1.0.0-beta4-SNAPSHOT + junit junit diff --git a/azure/src/main/java/com/microsoft/azure/management/Azure.java b/azure/src/main/java/com/microsoft/azure/management/Azure.java index 223d899588e5f..0b4c02d45ef87 100644 --- a/azure/src/main/java/com/microsoft/azure/management/Azure.java +++ b/azure/src/main/java/com/microsoft/azure/management/Azure.java @@ -57,6 +57,8 @@ import com.microsoft.azure.management.storage.implementation.StorageManager; import com.microsoft.azure.management.trafficmanager.TrafficManagerProfiles; import com.microsoft.azure.management.trafficmanager.implementation.TrafficManager; +import com.microsoft.azure.management.website.WebApps; +import com.microsoft.azure.management.website.implementation.AppServiceManager; import java.io.File; import java.io.IOException; @@ -75,6 +77,7 @@ public final class Azure { private final RedisManager redisManager; private final CdnManager cdnManager; private final DnsZoneManager dnsZoneManager; + private final AppServiceManager appServiceManager; private final SqlServerManager sqlServerManager; private final String subscriptionId; @@ -282,6 +285,7 @@ private Azure(RestClient restClient, String subscriptionId, String tenantId) { this.redisManager = RedisManager.authenticate(restClient, subscriptionId); this.cdnManager = CdnManager.authenticate(restClient, subscriptionId); this.dnsZoneManager = DnsZoneManager.authenticate(restClient, subscriptionId); + this.appServiceManager = AppServiceManager.authenticate(restClient, tenantId, subscriptionId); this.sqlServerManager = SqlServerManager.authenticate(restClient, subscriptionId); this.subscriptionId = subscriptionId; } @@ -489,6 +493,20 @@ public DnsZones dnsZones() { return dnsZoneManager.zones(); } + /** + * @return entry point to managing web apps. + */ + public WebApps webApps() { + return appServiceManager.webApps(); + } + + /** + * @return entry point to managing app services. + */ + public AppServiceManager appServices() { + return appServiceManager; + } + /** * @return entry point to managing Sql server. */ From e7f203393ba7aa729b0311b68296d28c9c83f949 Mon Sep 17 00:00:00 2001 From: Jianghao Lu Date: Wed, 30 Nov 2016 17:24:49 -0800 Subject: [PATCH 3/3] Supports deploying from Github, Git, Mercurial --- .../management/website/DomainContact.java | 2 +- .../azure/management/website/JavaVersion.java | 2 +- .../website/PublishingCredentials.java | 27 ++ .../azure/management/website/WebAppBase.java | 40 +- .../website/WebAppSourceControl.java | 345 ++++++++++++++++++ .../implementation/DeploymentSlotImpl.java | 30 ++ .../PublishingCredentialsImpl.java | 32 ++ .../implementation/WebAppBaseImpl.java | 91 ++++- .../website/implementation/WebAppImpl.java | 30 ++ .../WebAppSourceControlImpl.java | 148 ++++++++ .../website/implementation/WebAppsInner.java | 4 +- .../website/samples/ManageAppService.java | 9 + 12 files changed, 745 insertions(+), 15 deletions(-) create mode 100644 azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/PublishingCredentials.java create mode 100644 azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppSourceControl.java create mode 100644 azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/PublishingCredentialsImpl.java create mode 100644 azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppSourceControlImpl.java diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/DomainContact.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/DomainContact.java index d122fe3e4629d..8c1e838288862 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/DomainContact.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/DomainContact.java @@ -290,4 +290,4 @@ interface WithAttach extends Contact build(); } } - } +} diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/JavaVersion.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/JavaVersion.java index dc925068d9111..4716d196b5b83 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/JavaVersion.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/JavaVersion.java @@ -13,7 +13,7 @@ */ public final class JavaVersion { /** Static value 'Off' for JavaVersion. */ - public static final JavaVersion OFF = null; + public static final JavaVersion OFF = new JavaVersion(""); /** Static value Java 7 newest for JavaVersion. */ public static final JavaVersion JAVA_7_NEWEST = new JavaVersion("1.7"); diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/PublishingCredentials.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/PublishingCredentials.java new file mode 100644 index 0000000000000..d97bee39342fb --- /dev/null +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/PublishingCredentials.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + */ +package com.microsoft.azure.management.website; + +import com.microsoft.azure.management.apigeneration.Fluent; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; +import com.microsoft.azure.management.website.implementation.UserInner; + +/** + * A credential for publishing to a web app. + */ +@Fluent +public interface PublishingCredentials extends + Wrapper { + /** + * @return the username used for FTP and Git publishing. + */ + String username(); + + /** + * @return the password used for FTP and Git publishing. + */ + String password(); +} diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java index 864b9661c6bd3..73740ad947353 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppBase.java @@ -234,6 +234,10 @@ public interface WebAppBase> extends Map getConnectionStrings(); + PublishingCredentials getPublishingCredentials(); + + WebAppSourceControl getSourceControl(); + /** * Starts the web app or deployment slot. */ @@ -559,6 +563,18 @@ interface WithConnectionString { WithCreate withStickyConnectionString(String name, String value, ConnectionStringType type); } + /** + * A web app definition stage allowing source control to be set. + * @param the type of the resource, either a web app or a deployment slot + */ + interface WithSourceControl { + /** + * Starts the definition of a new source control. + * @return the first stage of a source control definition + */ + WebAppSourceControl.DefinitionStages.Blank> defineSourceControl(); + } + /** * A site definition with sufficient inputs to create a new web app / * deployments slot in the cloud, but exposing additional optional @@ -573,7 +589,8 @@ interface WithCreate extends WithClientCertEnabled, WithSiteConfigs, WithAppSettings, - WithConnectionString { + WithConnectionString, + WithSourceControl { } } @@ -891,6 +908,24 @@ interface WithConnectionString { */ Update withConnectionStringStickiness(String name, boolean sticky); } + + /** + * A web app update stage allowing source control to be set. + * @param the type of the resource, either a web app or a deployment slot + */ + interface WithSourceControl { + /** + * Starts the definition of a new source control. + * @return the first stage of a source control definition + */ + WebAppSourceControl.UpdateDefinitionStages.Blank> defineSourceControl(); + + /** + * Removes source control for deployment from the web app. + * @return the next stage of the web app update + */ + Update withoutSourceControl(); + } } /** @@ -906,6 +941,7 @@ interface Update extends UpdateStages.WithSiteEnabled, UpdateStages.WithSiteConfigs, UpdateStages.WithAppSettings, - UpdateStages.WithConnectionString { + UpdateStages.WithConnectionString, + UpdateStages.WithSourceControl { } } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppSourceControl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppSourceControl.java new file mode 100644 index 0000000000000..0e1ebcb28e71f --- /dev/null +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/WebAppSourceControl.java @@ -0,0 +1,345 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + */ +package com.microsoft.azure.management.website; + +import com.microsoft.azure.management.apigeneration.Fluent; +import com.microsoft.azure.management.resources.fluentcore.arm.models.ChildResource; +import com.microsoft.azure.management.resources.fluentcore.model.Attachable; +import com.microsoft.azure.management.resources.fluentcore.model.Wrapper; +import com.microsoft.azure.management.website.implementation.SiteSourceControlInner; + +/** + * A web app source control in a web app. + */ +@Fluent +public interface WebAppSourceControl extends + Wrapper, + ChildResource> { + /** + * @return the repository or source control url + */ + String repositoryUrl(); + + /** + * @return the name of the branch to use for deployment + */ + String branch(); + + /** + * @return whether to do manual or continuous integration + */ + boolean isManualIntegration(); + + /** + * @return whether deployment rollback is enabled + */ + boolean deploymentRollbackEnabled(); + + /** + * @return mercurial or Git repository type + */ + RepositoryType repositoryType(); + + /** + * The entirety of a web app source control definition. + * @param the return type of the final {@link Attachable#attach()} + */ + interface Definition extends + DefinitionStages.Blank, + DefinitionStages.WithAttach, + DefinitionStages.GitHubWithAttach, + DefinitionStages.WithRepositoryType, + DefinitionStages.WithExternalRepository, + DefinitionStages.WithBranch, + DefinitionStages.WithGitHubBranch { + } + + /** + * Grouping of web app source control definition stages applicable as part of a web app creation. + */ + interface DefinitionStages { + /** + * The first stage of a web app source control definition as part of a definition of a web app. + * @param the return type of the final {@link Attachable#attach()} + */ + interface Blank extends WithRepositoryType { + } + + /** + * A web app source control definition allowing repository type to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithRepositoryType { + /** + * Specifies the repository to be a public external repository, either Git or Mercurial. + * Continuous integration will not be turned on. + * @return + */ + WithExternalRepository withPublicExternalRepository(); + + /** + * Specifies the repository to be a GitHub repository. Continuous integration + * will be turned on. + * This repository can be either public or private, but your GitHub access token + * must have enough privileges to add a webhook to the repository. + * @param organization the user name or organization name the GitHub repository belongs to, e.g. Azure + * @param repository the name of the repository, e.g. azure-sdk-for-java + * @return the next stage of the web app source control definition + */ + WithBranch withGitHubRepository(String organization, String repository); + + /** + * Specifies the repository to be a GitHub repository. Continuous integration + * will be turned on. + * This repository can be either public or private, but your GitHub access token + * must have enough privileges to add a webhook to the repository. + * @param url the URL pointing to the repository, e.g. https://github.com/Azure/azure-sdk-for-java + * @return the next stage of the web app source control definition + */ + WithBranch withGitHubRepository(String url); + + /** + * Specifies the repository to be the implicitly created GitHub repository on + * the web app's hosting file system. The username / password information can + * be acquired from {@link WebAppBase#getPublishingCredentials()}. + * @return the next stage of the web app source control definition + */ + WithAttach withLocalGitRepository(); + } + + /** + * A web app source control definition stage allowing external source control information + * to be set. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithExternalRepository { + /** + * Specifies the repository to be a GIT repository. + * @return the next stage of the web app source control definition + */ + WithBranch withGit(String url); + + /** + * Specifies the repository to be a Mercurial repository. + * @return the next stage of the web app source control definition + */ + WithBranch withMercurial(String url); + } + + /** + * A web app source control definition allowing branch to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithBranch { + /** + * Specifies the branch in the repository to use. + * @param branch the branch to use + * @return the next stage of the web app source control definition + */ + WithAttach withBranch(String branch); + } + + /** + * A web app source control definition allowing branch to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithGitHubBranch { + /** + * Specifies the branch in the repository to use. + * @param branch the branch to use + * @return the next stage of the web app source control definition + */ + GitHubWithAttach withBranch(String branch); + } + + /** + * A web app source control definition allowing GitHub access token to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithGitHubAccessToken { + /** + * Specifies the GitHub personal access token. You can acquire one from + * https://github.com/settings/tokens. + * @param personalAccessToken the personal access token from GitHub. + * @return the next stage of the web app source control definition + */ + GitHubWithAttach withGitHubAccessToken(String personalAccessToken); + } + + /** The final stage of the web app source control definition. + *

+ * At this stage, any remaining optional settings can be specified, or the web app source control definition + * can be attached to the parent web app definition using {@link WithAttach#attach()}. + * @param the return type of {@link WithAttach#attach()} + */ + interface WithAttach extends + Attachable.InDefinition { + } + + /** The final stage of the web app source control definition that binds to a GitHub account. + *

+ * At this stage, any remaining optional settings can be specified, or the web app source control definition + * can be attached to the parent web app definition using {@link WithAttach#attach()}. + * @param the return type of {@link WithAttach#attach()} + */ + interface GitHubWithAttach extends + WithAttach, + WithGitHubAccessToken { + } + } + + /** The entirety of a web app source control definition as part of a web app update. + * @param the return type of the final {@link UpdateDefinitionStages.WithAttach#attach()} + */ + interface UpdateDefinition extends + UpdateDefinitionStages.Blank, + UpdateDefinitionStages.WithAttach, + UpdateDefinitionStages.GitHubWithAttach, + UpdateDefinitionStages.WithRepositoryType, + UpdateDefinitionStages.WithExternalRepository, + UpdateDefinitionStages.WithBranch, + UpdateDefinitionStages.WithGitHubBranch { + } + + /** + * Grouping of web app source control definition stages applicable as part of a web app update. + */ + interface UpdateDefinitionStages { + /** + * The first stage of a web app source control definition as part of an update of a web app. + * @param the return type of the final {@link Attachable#attach()} + */ + interface Blank extends WithRepositoryType { + } + + /** + * A web app source control definition allowing repository type to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithRepositoryType { + /** + * Specifies the repository to be a public external repository, either Git or Mercurial. + * Continuous integration will not be turned on. + * @return + */ + WithExternalRepository withPublicExternalRepository(); + + /** + * Specifies the repository to be a GitHub repository. Continuous integration + * will be turned on. + * This repository can be either public or private, but your GitHub access token + * must have enough privileges to add a webhook to the repository. + * @param organization the user name or organization name the GitHub repository belongs to, e.g. Azure + * @param repository the name of the repository, e.g. azure-sdk-for-java + * @return the next stage of the web app source control definition + */ + WithBranch withGitHubRepository(String organization, String repository); + + /** + * Specifies the repository to be a GitHub repository. Continuous integration + * will be turned on. + * This repository can be either public or private, but your GitHub access token + * must have enough privileges to add a webhook to the repository. + * @param url the URL pointing to the repository, e.g. https://github.com/Azure/azure-sdk-for-java + * @return the next stage of the web app source control definition + */ + WithGitHubBranch withGitHubRepository(String url); + + /** + * Specifies the repository to be the implicitly created GitHub repository on + * the web app's hosting file system. The username / password information can + * be acquired from {@link WebAppBase#getPublishingCredentials()}. + * @return the next stage of the web app source control definition + */ + WithAttach withLocalGitRepository(); + } + + interface WithExternalRepository { + /** + * Specifies the repository to be a GIT repository. + * @return the next stage of the web app source control definition + */ + WithBranch withGit(String url); + + /** + * Specifies the repository to be a Mercurial repository. + * @return the next stage of the web app source control definition + */ + WithBranch withMercurial(String url); + } + + /** + * A web app source control definition allowing branch to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithBranch { + /** + * Specifies the branch in the repository to use. + * @param branch the branch to use + * @return the next stage of the web app source control definition + */ + WithAttach withBranch(String branch); + } + + /** + * A web app source control definition allowing branch to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithGitHubBranch { + /** + * Specifies the branch in the repository to use. + * @param branch the branch to use + * @return the next stage of the web app source control definition + */ + GitHubWithAttach withBranch(String branch); + } + + /** + * A web app source control definition allowing GitHub access token to be specified. + * @param the return type of the final {@link WithAttach#attach()} + */ + interface WithGitHubAccessToken { + /** + * Specifies the GitHub personal access token. You can acquire one from + * https://github.com/settings/tokens. + * @param personalAccessToken the personal access token from GitHub. + * @return the next stage of the web app source control definition + */ + GitHubWithAttach withGitHubAccessToken(String personalAccessToken); + } + + /** The final stage of the web app source control definition. + *

+ * At this stage, any remaining optional settings can be specified, or the web app source control definition + * can be attached to the parent web app update using {@link WithAttach#attach()}. + * @param the return type of {@link WithAttach#attach()} + */ + interface WithAttach extends + Attachable.InUpdate { + } + + /** The final stage of the web app source control definition that binds to a GitHub account. + *

+ * At this stage, any remaining optional settings can be specified, or the web app source control definition + * can be attached to the parent web app update using {@link WithAttach#attach()}. + * @param the return type of {@link WithAttach#attach()} + */ + interface GitHubWithAttach extends + WithAttach, + WithGitHubAccessToken { + } + } + + /** + * The type of a repository. + */ + enum RepositoryType { + /** Git repository. */ + GIT, + /** Mercurial repository. */ + MERCURIAL + } + } diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DeploymentSlotImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DeploymentSlotImpl.java index 814c4fea36cf7..719c438cc6d68 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DeploymentSlotImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/DeploymentSlotImpl.java @@ -10,8 +10,11 @@ import com.google.common.collect.Maps; import com.microsoft.azure.management.website.DeploymentSlot; import com.microsoft.azure.management.website.HostNameBinding; +import com.microsoft.azure.management.website.PublishingCredentials; import com.microsoft.azure.management.website.WebApp; +import com.microsoft.azure.management.website.WebAppSourceControl; import rx.Observable; +import rx.functions.Func1; import java.util.ArrayList; import java.util.List; @@ -158,6 +161,11 @@ Observable updateSlotConfigurations(SlotConfigName return client.updateSlotConfigurationNamesAsync(resourceGroupName(), parent().name(), inner); } + @Override + Observable createOrUpdateSourceControl(SiteSourceControlInner inner) { + return client.createOrUpdateSourceControlSlotAsync(resourceGroupName(), parent().name(), name(), inner); + } + @Override public void swap(String slotName) { client.swapSlotsSlot(resourceGroupName(), parent().name(), name(), new CsmSlotEntityInner().withTargetSlot(slotName)); @@ -172,4 +180,26 @@ public void applySlotConfigurations(String slotName) { public void resetSlotConfigurations() { client.resetSlotConfigurationSlot(resourceGroupName(), parent().name(), name()); } + + @Override + Observable deleteSourceControl() { + return client.deleteSourceControlSlotAsync(resourceGroupName(), parent().name(), name()).map(new Func1() { + @Override + public Void call(Object o) { + return null; + } + }); + } + + @Override + public PublishingCredentials getPublishingCredentials() { + UserInner inner = client.listPublishingCredentialsSlot(resourceGroupName(), parent().name(), name()); + return new PublishingCredentialsImpl(inner); + } + + @Override + public WebAppSourceControl getSourceControl() { + SiteSourceControlInner siteSourceControlInner = client.getSourceControlSlot(resourceGroupName(), parent().name(), name()); + return new WebAppSourceControlImpl<>(siteSourceControlInner, this, myManager); + } } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/PublishingCredentialsImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/PublishingCredentialsImpl.java new file mode 100644 index 0000000000000..ede4b56327c9d --- /dev/null +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/PublishingCredentialsImpl.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + */ +package com.microsoft.azure.management.website.implementation; + +import com.microsoft.azure.management.apigeneration.Fluent; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.WrapperImpl; +import com.microsoft.azure.management.website.PublishingCredentials; + +/** + * A credential for publishing to a web app. + */ +@Fluent +class PublishingCredentialsImpl extends + WrapperImpl + implements PublishingCredentials { + PublishingCredentialsImpl(UserInner innerObject) { + super(innerObject); + } + + @Override + public String username() { + return inner().publishingUserName(); + } + + @Override + public String password() { + return inner().publishingPassword(); + } +} diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java index 695fc7bccf81d..ef3f0955b795d 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppBaseImpl.java @@ -82,6 +82,8 @@ abstract class WebAppBaseImpl< private Map connectionStringsToAdd; private List connectionStringsToRemove; private Map connectionStringStickiness; + private WebAppSourceControlImpl sourceControl; + private boolean sourceControlToDelete; WebAppBaseImpl(String name, SiteInner innerObject, SiteConfigInner configObject, final WebAppsInner client, AppServiceManager manager) { super(name, innerObject, manager); @@ -94,12 +96,14 @@ abstract class WebAppBaseImpl< private FluentT normalizeProperties() { this.hostNameBindingsToCreate = new HashMap<>(); this.hostNameBindingsToDelete = new ArrayList<>(); - appSettingsToAdd = new HashMap<>(); - appSettingsToRemove = new ArrayList<>(); - appSettingStickiness = new HashMap<>(); - connectionStringsToAdd = new HashMap<>(); - connectionStringsToRemove = new ArrayList<>(); - connectionStringStickiness = new HashMap<>(); + this.appSettingsToAdd = new HashMap<>(); + this.appSettingsToRemove = new ArrayList<>(); + this.appSettingStickiness = new HashMap<>(); + this.connectionStringsToAdd = new HashMap<>(); + this.connectionStringsToRemove = new ArrayList<>(); + this.connectionStringStickiness = new HashMap<>(); + this.sourceControl = null; + this.sourceControlToDelete = false; this.sslBindingsToCreate = new HashMap<>(); if (inner().hostNames() != null) { this.hostNamesSet = Sets.newHashSet(inner().hostNames()); @@ -412,6 +416,10 @@ public ConnectionString apply(String input) { abstract Observable updateSlotConfigurations(SlotConfigNamesResourceInner inner); + abstract Observable createOrUpdateSourceControl(SiteSourceControlInner inner); + + abstract Observable deleteSourceControl(); + @Override public Observable createResourceAsync() { if (hostNameSslStateMap.size() > 0) { @@ -423,11 +431,23 @@ public Observable createResourceAsync() { .map(new Func1() { @Override public String call(AppServicePlan appServicePlan) { - inner().withLocation(appServicePlan.regionName()); return appServicePlan.regionName(); } }); } + locationObs = locationObs.map(new Func1() { + @Override + public String call(String s) { + inner().withLocation(s); + if (sourceControl != null) { + sourceControl.inner().withLocation(s); + } + if (inner().siteConfig() != null) { + inner().siteConfig().withLocation(s); + } + return s; + } + }); // Construct web app observable return locationObs.flatMap(new Func1>() { @Override @@ -441,8 +461,6 @@ public Observable call(String s) { public Observable call(final SiteInner siteInner) { if (inner().siteConfig() == null) { return Observable.just(siteInner); - } else if (inner().siteConfig().location() == null) { - inner().siteConfig().withLocation(inner().location()); } return createOrUpdateSiteConfig(inner().siteConfig()) .flatMap(new Func1>() { @@ -631,6 +649,43 @@ public SiteInner call(SlotConfigNamesResourceInner slotConfigNamesResourceInner) return observable; } }) + // create source control + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner inner) { + if (sourceControl == null || sourceControlToDelete) { + return Observable.just(inner); + } + return sourceControl.registerGithubAccessToken() + .flatMap(new Func1>() { + @Override + public Observable call(SourceControlInner sourceControlInner) { + return createOrUpdateSourceControl(sourceControl.inner()); + } + }) + .map(new Func1() { + @Override + public SiteInner call(SiteSourceControlInner siteSourceControlInner) { + return inner; + } + }); + } + }) + // delete source control + .flatMap(new Func1>() { + @Override + public Observable call(final SiteInner inner) { + if (!sourceControlToDelete) { + return Observable.just(inner); + } + return deleteSourceControl().map(new Func1() { + @Override + public SiteInner call(Void aVoid) { + return inner; + } + }); + } + }) // convert from inner .map(new Func1() { @Override @@ -990,4 +1045,22 @@ public FluentImplT withConnectionStringStickiness(String name, boolean stickines connectionStringStickiness.put(name, stickiness); return (FluentImplT) this; } + + @SuppressWarnings("unchecked") + FluentImplT withSourceControl(WebAppSourceControlImpl sourceControl) { + this.sourceControl = sourceControl; + return (FluentImplT) this; + } + + @Override + public WebAppSourceControlImpl defineSourceControl() { + return new WebAppSourceControlImpl<>(new SiteSourceControlInner(), this, myManager); + } + + @Override + @SuppressWarnings("unchecked") + public FluentImplT withoutSourceControl() { + sourceControlToDelete = true; + return (FluentImplT) this; + } } \ No newline at end of file diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java index 34bf14688760a..e44eaaac9e16c 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppImpl.java @@ -13,8 +13,11 @@ import com.microsoft.azure.management.website.AppServicePricingTier; import com.microsoft.azure.management.website.DeploymentSlots; import com.microsoft.azure.management.website.HostNameBinding; +import com.microsoft.azure.management.website.PublishingCredentials; import com.microsoft.azure.management.website.WebApp; +import com.microsoft.azure.management.website.WebAppSourceControl; import rx.Observable; +import rx.functions.Func1; import java.util.ArrayList; import java.util.Collections; @@ -89,6 +92,21 @@ Observable updateSlotConfigurations(SlotConfigName return client.updateSlotConfigurationNamesAsync(resourceGroupName(), name(), inner); } + @Override + Observable createOrUpdateSourceControl(SiteSourceControlInner inner) { + return client.createOrUpdateSourceControlAsync(resourceGroupName(), name(), inner); + } + + @Override + Observable deleteSourceControl() { + return client.deleteSourceControlAsync(resourceGroupName(), name()).map(new Func1() { + @Override + public Void call(Object o) { + return null; + } + }); + } + @Override public DeploymentSlots deploymentSlots() { if (deploymentSlots == null) { @@ -112,6 +130,18 @@ public String apply(HostNameBinding input) { })); } + @Override + public PublishingCredentials getPublishingCredentials() { + UserInner inner = client.listPublishingCredentials(resourceGroupName(), name()); + return new PublishingCredentialsImpl(inner); + } + + @Override + public WebAppSourceControl getSourceControl() { + SiteSourceControlInner siteSourceControlInner = client.getSourceControl(resourceGroupName(), name()); + return new WebAppSourceControlImpl<>(siteSourceControlInner, this, myManager); + } + @Override public void start() { client.start(resourceGroupName(), name()); diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppSourceControlImpl.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppSourceControlImpl.java new file mode 100644 index 0000000000000..406fd7659b271 --- /dev/null +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppSourceControlImpl.java @@ -0,0 +1,148 @@ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for + * license information. + */ +package com.microsoft.azure.management.website.implementation; + +import com.microsoft.azure.management.apigeneration.LangDefinition; +import com.microsoft.azure.management.resources.fluentcore.model.implementation.IndexableWrapperImpl; +import com.microsoft.azure.management.resources.fluentcore.utils.Utils; +import com.microsoft.azure.management.website.WebAppBase; +import com.microsoft.azure.management.website.WebAppSourceControl; +import retrofit2.http.Body; +import retrofit2.http.Headers; +import retrofit2.http.PUT; +import retrofit2.http.Path; +import retrofit2.http.Query; +import rx.Observable; + +/** + * Implementation for {@link WebAppSourceControl} and its create and update interfaces. + */ +@LangDefinition +class WebAppSourceControlImpl< + FluentT extends WebAppBase, + FluentImplT extends WebAppBaseImpl> + extends IndexableWrapperImpl + implements + WebAppSourceControl, + WebAppSourceControl.Definition>, + WebAppSourceControl.UpdateDefinition> { + + private WebAppBaseImpl parent; + private String githubAccessToken; + private SourceControlService sourceControlService; + + WebAppSourceControlImpl(SiteSourceControlInner inner, WebAppBaseImpl parent, AppServiceManager manager) { + super(inner); + this.parent = parent; + this.sourceControlService = manager.restClient().retrofit().create(SourceControlService.class); + } + + @Override + public String name() { + return inner().name(); + } + + @Override + public String repositoryUrl() { + return inner().repoUrl(); + } + + @Override + public String branch() { + return inner().branch(); + } + + @Override + public boolean isManualIntegration() { + return Utils.toPrimitiveBoolean(inner().isManualIntegration()); + } + + @Override + public boolean deploymentRollbackEnabled() { + return Utils.toPrimitiveBoolean(inner().deploymentRollbackEnabled()); + } + + @Override + public RepositoryType repositoryType() { + if (inner().isMercurial() == null) { + return null; + } else { + return inner().isMercurial() ? RepositoryType.MERCURIAL : RepositoryType.GIT; + } + } + + @Override + public FluentImplT attach() { + parent().withSourceControl(this); + return parent(); + } + + @Override + public WebAppSourceControlImpl withGit(String url) { + inner().withIsMercurial(false).withRepoUrl(url); + return this; + } + + @Override + public WebAppSourceControlImpl withMercurial(String url) { + inner().withIsMercurial(true).withRepoUrl(url); + return this; + } + + @Override + @SuppressWarnings("unchecked") + public FluentImplT parent() { + return (FluentImplT) parent; + } + + @Override + public WebAppSourceControlImpl withBranch(String branch) { + inner().withBranch(branch); + return this; + } + + @Override + public WebAppSourceControlImpl withPublicExternalRepository() { + inner().withIsManualIntegration(true); + return this; + } + + @Override + public WebAppSourceControlImpl withGitHubRepository(String organization, String repository) { + return withGitHubRepository(String.format("https://github.com/%s/%s", organization, repository)); + } + + @Override + public WebAppSourceControlImpl withGitHubRepository(String url) { + inner().withRepoUrl(url).withIsMercurial(false).withIsManualIntegration(false); + return this; + } + + @Override + public WebAppSourceControlImpl withLocalGitRepository() { + inner().withRepoUrl(null).withBranch(null).withIsMercurial(false).withIsManualIntegration(false); + return this; + } + + @Override + public WebAppSourceControlImpl withGitHubAccessToken(String personalAccessToken) { + this.githubAccessToken = personalAccessToken; + return this; + } + + Observable registerGithubAccessToken() { + if (githubAccessToken == null) { + return Observable.just(null); + } + return sourceControlService.updateSourceControl("Github", new SourceControlInner().withToken(githubAccessToken), "2016-03-01"); + } + + private interface SourceControlService { + @Headers("Content-Type: application/json; charset=utf-8") + @PUT("/providers/Microsoft.Web/sourcecontrols/{sourceControlIdentifier}") + Observable updateSourceControl(@Path("sourceControlIdentifier") String sourceControlIdentifier, @Body SourceControlInner sourceControl, @Query("api-version") String apiVersion); + } +} diff --git a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppsInner.java b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppsInner.java index f1cf128d4c6a6..7dae24f7e0dcd 100644 --- a/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppsInner.java +++ b/azure-mgmt-website/src/main/java/com/microsoft/azure/management/website/implementation/WebAppsInner.java @@ -19670,7 +19670,7 @@ public Observable> call(Response updateSourceControlSlotDelegate(Response response) throws CloudException, IOException, IllegalArgumentException { return new AzureServiceResponseBuilder(this.client.mapperAdapter()) - .register(200, new TypeToken() { }.getType()) + .register(201, new TypeToken() { }.getType()) .registerError(CloudException.class) .build(response); } @@ -21588,7 +21588,7 @@ public Observable> call(Response createOrUpdateSourceControlDelegate(Response response) throws CloudException, IOException, IllegalArgumentException { return new AzureServiceResponseBuilder(this.client.mapperAdapter()) - .register(200, new TypeToken() { }.getType()) + .register(201, new TypeToken() { }.getType()) .registerError(CloudException.class) .build(response); } diff --git a/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java index 4af585ef106eb..b8c0222dadb91 100644 --- a/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java +++ b/azure-samples/src/main/java/com/microsoft/azure/management/website/samples/ManageAppService.java @@ -66,6 +66,15 @@ public static void main(String[] args) { .authenticate(credFile) .withDefaultSubscription(); + azure.webApps().getByGroup("javacsmrg319", "java-webapp-319") + .update() + .defineSourceControl() + .withPublicExternalRepository() + .withGit("https://github.com/jianghaolu/azure-site-test") + .withBranch("master") + .attach() + .apply(); + // Print selected subscription System.out.println("Selected subscription: " + azure.subscriptionId()); try {