Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

support deployment status on web app #1339

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e4b9cb7
init impl for deployment status
weidongxu-microsoft Dec 17, 2020
295f4d7
rename method to zipDeployWithResponse
weidongxu-microsoft Dec 28, 2020
edacd4a
add BuildStatus enum
weidongxu-microsoft Dec 28, 2020
39811e3
update
weidongxu-microsoft Dec 28, 2020
a98b88f
complete the interface
weidongxu-microsoft Dec 28, 2020
5da6d39
move test code
weidongxu-microsoft Dec 28, 2020
efc8467
add ignore
weidongxu-microsoft Dec 28, 2020
049765a
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Jan 11, 2021
3f84f38
rename method to pushZipDeploy
weidongxu-microsoft Jan 18, 2021
4c88320
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Feb 26, 2021
6c8f19b
add 202 to OK response for deploymentStatus
weidongxu-microsoft Feb 26, 2021
383687a
correct json property
weidongxu-microsoft Feb 26, 2021
0177bb8
nit
weidongxu-microsoft Feb 26, 2021
de77c10
rename
weidongxu-microsoft Feb 26, 2021
e70fede
nit
weidongxu-microsoft Feb 26, 2021
6cd0e16
loop on 10 sec wait until depolyment status available
weidongxu-microsoft Feb 26, 2021
095a820
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Mar 5, 2021
98d6c33
push deploy for onedeploy
weidongxu-microsoft Mar 5, 2021
40b5934
change sample to use onedeploy
weidongxu-microsoft Mar 5, 2021
97e334f
update sample
weidongxu-microsoft Mar 5, 2021
a4e799a
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Mar 8, 2021
d5a89b4
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Mar 23, 2021
6e658df
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Mar 30, 2021
7503db6
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft Mar 31, 2021
8483cc7
add trackDeploymentProgress
weidongxu-microsoft May 6, 2021
d5b811d
add trackDeploymentProgress
weidongxu-microsoft May 6, 2021
1d8b879
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft May 7, 2021
5fb2194
Merge branch 'appservice-deployment-status' of https://github.com/Azu…
weidongxu-microsoft May 7, 2021
81b1eac
correct interface
weidongxu-microsoft May 7, 2021
7845058
option for trackDeploymentProgress (#1395)
weidongxu-microsoft May 7, 2021
63612a6
Merge branch 'master' into appservice-deployment-status
weidongxu-microsoft May 10, 2021
8d601e4
update api-version and test
weidongxu-microsoft Dec 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.appservice;

/**
* Result of async deployment.
*/
public class AsyncDeploymentResult {

private final String deploymentId;

public AsyncDeploymentResult(String deploymentId) {
this.deploymentId = deploymentId;
}

public String getDeploymentId() {
return deploymentId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* 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.appservice;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.microsoft.azure.management.resources.fluentcore.arm.ExpandableStringEnum;

import java.util.Collection;

public final class BuildStatus extends ExpandableStringEnum<BuildStatus> {

/** Enum value RuntimeFailed. */
public static final BuildStatus RUNTIME_FAILED = fromString("RuntimeFailed");

/** Enum value BuildAborted. */
public static final BuildStatus BUILD_ABORTED = fromString("BuildAborted");

/** Enum value BuildFailed. */
public static final BuildStatus BUILD_FAILED = fromString("BuildFailed");

/** Enum value BuildRequestReceived. */
public static final BuildStatus BUILD_REQUEST_RECEIVED = fromString("BuildRequestReceived");

/** Enum value BuildPending. */
public static final BuildStatus BUILD_PENDING = fromString("BuildPending");

/** Enum value BuildInProgress. */
public static final BuildStatus BUILD_IN_PROGRESS = fromString("BuildInProgress");

/** Enum value BuildSuccessful. */
public static final BuildStatus BUILD_SUCCESSFUL = fromString("BuildSuccessful");

/** Enum value PostBuildRestartRequired. */
public static final BuildStatus POST_BUILD_RESTART_REQUIRED = fromString("PostBuildRestartRequired");

/** Enum value RuntimeStarting. */
public static final BuildStatus RUNTIME_STARTING = fromString("RuntimeStarting");

/** Enum value RuntimeSuccessful. */
public static final BuildStatus RUNTIME_SUCCESSFUL = fromString("RuntimeSuccessful");

/**
* Creates or finds a BuildStatus from its string representation.
* @param name a name to look for
* @return the corresponding BuildStatus
*/
@JsonCreator
public static BuildStatus fromString(String name) {
return fromString(name, BuildStatus.class);
}

/**
* @return known SecurityRuleDirection values
*/
public static Collection<BuildStatus> values() {
return values(BuildStatus.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* 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.appservice;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.microsoft.azure.ProxyResource;
import com.microsoft.rest.serializer.JsonFlatten;

@JsonFlatten
public class DeploymentStatus extends ProxyResource {
Copy link
Member Author

Choose a reason for hiding this comment

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

Response class for deploymentStatus. Note this will get changed after swagger updated by service.


@JsonProperty(value = "properties.id")
private BuildStatus deploymentId;

@JsonProperty(value = "properties.buildStatus")
private BuildStatus buildStatus;

@JsonProperty(value = "properties.numberOfInstancesInProgress")
int numberOfInstancesInProgress;

@JsonProperty(value = "properties.numberOfInstancesSuccessful")
int numberOfInstancesSuccessful;

@JsonProperty(value = "properties.numberOfInstancesFailed")
int numberOfInstancesFailed;

public BuildStatus deploymentId() {
return deploymentId;
}

public BuildStatus buildStatus() {
return buildStatus;
}

public int numberOfInstancesInProgress() {
return numberOfInstancesInProgress;
}

public int numberOfInstancesSuccessful() {
return numberOfInstancesSuccessful;
}

public int numberOfInstancesFailed() {
return numberOfInstancesFailed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import com.microsoft.azure.management.resources.fluentcore.model.Refreshable;
import com.microsoft.azure.management.resources.fluentcore.model.Updatable;
import rx.Completable;
import rx.Observable;

import java.io.File;
import java.io.InputStream;
Expand Down Expand Up @@ -525,4 +526,56 @@ interface Update extends
WebAppBase.Update<WebApp>,
UpdateStages.WithDockerContainerImage {
}
}

/**
* Asynchronous deploys a ZIP file onto the Azure specialized Java SE image on this web app.
*
* @see #getDeploymentStatusAsync(String)
* @param zipFile the ZIP file to upload
* @return the result of the deployment, which contains the deployment ID for query on the deployment status.
*/
Observable<AsyncDeploymentResult> zipDeployWithResponseAsync(File zipFile);

/**
* Asynchronous deploys a ZIP file onto the Azure specialized Java SE image on this web app.
*
* @see #getDeploymentStatusAsync(String)
* @param zipFile the ZIP file to upload
* @return the result of the deployment, which contains the deployment ID for query on the deployment status.
*/
Observable<AsyncDeploymentResult> zipDeployWithResponseAsync(InputStream zipFile);

/**
* Gets the deployment status of the web app.
*
* @param deploymentId the deployment ID of the web app.
* @return the deployment status.
*/
Observable<DeploymentStatus> getDeploymentStatusAsync(String deploymentId);

/**
* Asynchronous deploys a ZIP file onto the Azure specialized Java SE image on this web app.
*
* @see #getDeploymentStatusAsync(String)
* @param zipFile the ZIP file to upload
* @return the result of the deployment, which contains the deployment ID for query on the deployment status.
*/
AsyncDeploymentResult zipDeployWithResponse(File zipFile);

/**
* Asynchronous deploys a ZIP file onto the Azure specialized Java SE image on this web app.
*
* @see #getDeploymentStatus(String)
* @param zipFile the ZIP file to upload
* @return the result of the deployment, which contains the deployment ID for query on the deployment status.
*/
AsyncDeploymentResult zipDeployWithResponse(InputStream zipFile);
weidongxu-microsoft marked this conversation as resolved.
Show resolved Hide resolved

/**
* Gets the deployment status of the web app.
*
* @param deploymentId the deployment ID of the web app.
* @return the deployment status.
*/
DeploymentStatus getDeploymentStatus(String deploymentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@

package com.microsoft.azure.management.appservice.implementation;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Joiner;
import com.google.common.io.ByteStreams;
import com.google.common.reflect.TypeToken;
import com.microsoft.azure.AzureResponseBuilder;
import com.microsoft.azure.management.appservice.AsyncDeploymentResult;
import com.microsoft.azure.management.appservice.DeployType;
import com.microsoft.azure.management.appservice.WebAppBase;
import com.microsoft.rest.RestClient;
Expand All @@ -28,6 +30,7 @@
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
import rx.Completable;
import rx.Emitter;
import rx.Emitter.BackpressureMode;
Expand Down Expand Up @@ -103,13 +106,21 @@ private interface KuduService {
@POST("api/zipdeploy")
Observable<Response<ResponseBody>> zipDeploy(@Body RequestBody zipFile);

@Headers({ "Content-Type: application/octet-stream", "x-ms-logging-context: com.microsoft.azure.management.appservice.WebApps zipDeploy", "x-ms-body-logging: false" })
@POST("api/zipdeploy")
Observable<Response<ResponseBody>> zipDeploy(@Body RequestBody zipFile, @Query("isAsync") Boolean isAsync);

@Headers({ "Content-Type: application/octet-stream", "x-ms-logging-context: com.microsoft.azure.management.appservice.WebApps publish", "x-ms-body-logging: false" })
@POST("api/publish")
Observable<Response<ResponseBody>> deploy(@Body RequestBody file, @Query("type") DeployType type, @Query("path") String path, @Query("restart") Boolean restart, @Query("clean") Boolean clean);

@Headers({ "x-ms-logging-context: com.microsoft.azure.management.appservice.WebApps settings" })
@GET("api/settings")
Observable<Response<ResponseBody>> settings();

@Headers({ "x-ms-logging-context: com.microsoft.azure.management.appservice.WebApps getDeployment" })
@GET
Observable<Response<ResponseBody>> getDeployment(@Url String deploymentUrl);
}

Observable<String> streamApplicationLogsAsync() {
Expand Down Expand Up @@ -205,6 +216,91 @@ Completable zipDeployAsync(InputStream zipFile) {
}
}

private static class DeploymentIntermediateResult {
private String deploymentUrl;
private String deploymentId;
}

private static class DeploymentResponse {
@JsonProperty(value = "id")
private String id;

@JsonProperty(value = "is_temp")
private Boolean isTemp;
}

Observable<AsyncDeploymentResult> zipDeployWithResponseAsync(InputStream zipFile) {
final long pollIntervalInSeconds = 5;
final long pollCount = 3 * 60 / pollIntervalInSeconds; // 3 minutes

try {
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), ByteStreams.toByteArray(zipFile));
Observable<ServiceResponse<DeploymentIntermediateResult>> responseDeployment =
retryOnError(handleResponse(service.zipDeploy(body, true), new Func1<Response<ResponseBody>, DeploymentIntermediateResult>() {
@Override
public DeploymentIntermediateResult call(Response<ResponseBody> responseBodyResponse) {
DeploymentIntermediateResult result = new DeploymentIntermediateResult();
result.deploymentUrl = responseBodyResponse.headers().get("Location");
return result;
}
}));

Observable<AsyncDeploymentResult> result = responseDeployment.flatMap(new Func1<ServiceResponse<DeploymentIntermediateResult>, Observable<AsyncDeploymentResult>>() {
@Override
public Observable<AsyncDeploymentResult> call(ServiceResponse<DeploymentIntermediateResult> responseDeployment) {
DeploymentIntermediateResult result = responseDeployment.body();
if (result.deploymentUrl == null || result.deploymentUrl.isEmpty()) {
// error if URL not available
return Observable.error(new RestException("Deployment URL not found in response", responseDeployment.response()));
} else {
// delay for poll
Observable<DeploymentIntermediateResult> delayedResult = Observable.just(result).delaySubscription(pollIntervalInSeconds, TimeUnit.SECONDS);
return delayedResult.flatMap(new Func1<DeploymentIntermediateResult, Observable<ServiceResponse<DeploymentIntermediateResult>>>() {
@Override
public Observable<ServiceResponse<DeploymentIntermediateResult>> call(DeploymentIntermediateResult deployment) {
return handleResponse(service.getDeployment(deployment.deploymentUrl), new Func1<Response<ResponseBody>, DeploymentIntermediateResult>() {
@Override
public DeploymentIntermediateResult call(Response<ResponseBody> response) {
DeploymentIntermediateResult result = new DeploymentIntermediateResult();
try {
DeploymentResponse deployment = restClient.serializerAdapter().deserialize(response.body().string(), DeploymentResponse.class);
if (deployment.id != null && !deployment.id.isEmpty() && deployment.isTemp != null && !deployment.isTemp) {
result.deploymentId = deployment.id;
}
} catch (IOException e) {
//
}
return result;
}
});
}
}).repeat(pollCount).takeUntil(new Func1<ServiceResponse<DeploymentIntermediateResult>, Boolean>() {
@Override
public Boolean call(ServiceResponse<DeploymentIntermediateResult> response) {
return response.body().deploymentId != null;
}
}).last().flatMap(new Func1<ServiceResponse<DeploymentIntermediateResult>, Observable<AsyncDeploymentResult>>() {
@Override
public Observable<AsyncDeploymentResult> call(ServiceResponse<DeploymentIntermediateResult> response) {
DeploymentIntermediateResult result = response.body();
if (result.deploymentId != null) {
return Observable.just(new AsyncDeploymentResult(result.deploymentId));
} else {
// error if ID not available
return Observable.error(new RestException("Timeout while polling deployment ID", response.response()));
}
}
});
}
}
});

return result;
} catch (IOException e) {
return Observable.error(e);
}
}

Completable deployAsync(DeployType type, InputStream file, String path, Boolean restart, Boolean clean) {
try {
RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), ByteStreams.toByteArray(file));
Expand Down Expand Up @@ -248,12 +344,21 @@ public Map<String, String> call(ServiceResponse<Map<String, String>> response) {
}

private Observable<ServiceResponse<Void>> handleResponse(Observable<Response<ResponseBody>> observable) {
return observable.flatMap(new Func1<Response<ResponseBody>, Observable<ServiceResponse<Void>>>() {
return this.handleResponse(observable, new Func1<Response<ResponseBody>, Void>() {
@Override
public Void call(Response<ResponseBody> responseBodyResponse) {
return null;
}
});
}

private <T> Observable<ServiceResponse<T>> handleResponse(Observable<Response<ResponseBody>> observable, final Func1<Response<ResponseBody>, T> responseFunc) {
return observable.flatMap(new Func1<Response<ResponseBody>, Observable<ServiceResponse<T>>>() {
@Override
public Observable<ServiceResponse<Void>> call(Response<ResponseBody> response) {
public Observable<ServiceResponse<T>> call(Response<ResponseBody> response) {
try {
if (response.isSuccessful()) {
return Observable.just(new ServiceResponse<Void>(null, response));
return Observable.just(new ServiceResponse<T>(responseFunc.call(response), response));
} else {
String errorMessage = "Status code " + response.code();
if (response.errorBody() != null
Expand Down
Loading