hawkBit rest docs (management & DDI API) (#688)

* hawkBit REST docs.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fiy gitignore.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Add to website.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Switch to generated docs.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Fix typos.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Review findings.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Otimizations.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Revert accidental checkin.

Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>

* Add security link.
This commit is contained in:
Kai Zimmermann
2018-05-24 16:10:01 +02:00
committed by GitHub
parent 7126d68f31
commit 428e3af2bc
244 changed files with 11636 additions and 430 deletions

View File

@@ -0,0 +1,16 @@
# Eclipse.IoT hawkBit - Direct Device Integration API - Model and Resources
The Direct Device Integration (DDI) API is used by devices for communicating with the HawkBit Update Server through HTTP.
# Version 1
The model follows [semantic versioning](http://semver.org) with MAJOR version, i.e. breaking changes will result in a new MAJOR version.
# Compile
#### Build hawkbit-ddi-api
```
$ cd hawkbit/hawkbit-ddi-api
$ mvn clean install
```

View File

@@ -0,0 +1,37 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-parent</artifactId>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-ddi-api</artifactId>
<name>hawkBit :: REST :: DDI API</name>
<dependencies>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* <p>
* After the HawkBit Target has executed an action, received by a GET(URL)
* request it reports the completion of it to the HawkBit Server with a action
* status message, i.e. with a PUT message to the feedback channel, i.e. PUT
* URL/feedback. This message could be used not only at the end of execution but
* also as status updates during a longer lasting execution period. The format
* of each action answer message is defined below at each action. But it is
* expected, that the contents of the message answers have all a similar
* structure: The content starts with a generic header and additional elements.
* *
* </p>
*
* <p>
* The answer header would look like: { "id": "51659181", "time":
* "20140511T121314", "status": { "execution": "closed", "result": { "final":
* "success", "progress": {} } "details": [], } }
* </p>
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class DdiActionFeedback {
private final Long id;
private final String time;
@NotNull
@Valid
private final DdiStatus status;
/**
* Constructor.
*
* @param id
* of the actions the feedback is for
* @param time
* of the feedback
* @param status
* is the feedback itself
*/
@JsonCreator
public DdiActionFeedback(@JsonProperty("id") final Long id, @JsonProperty("time") final String time,
@JsonProperty("status") final DdiStatus status) {
this.id = id;
this.time = time;
this.status = status;
}
public Long getId() {
return id;
}
public String getTime() {
return time;
}
public DdiStatus getStatus() {
return status;
}
@Override
public String toString() {
return "ActionFeedback [id=" + id + ", time=" + time + ", status=" + status + "]";
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) Siemens AG, 2017
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.List;
import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/**
* Provide action history information to the controller as part of response to
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}: 1.
* Current action status at the server; 2. List of messages from action history
* that were sent to server earlier by the controller using
* {@link DdiActionFeedback}.
*/
@JsonPropertyOrder({ "status", "messages" })
public class DdiActionHistory {
@JsonProperty("status")
private final String actionStatus;
@JsonProperty("messages")
private final List<String> messages;
/**
* Parameterized constructor for creating {@link DdiActionHistory}.
*
* @param actionStatus
* is the current action status at the server
* @param messages
* is a list of messages retrieved from action history.
*/
@JsonCreator
public DdiActionHistory(@JsonProperty("status") final String actionStatus,
@JsonProperty("messages") List<String> messages) {
this.actionStatus = actionStatus;
this.messages = messages;
}
@Override
public String toString() {
return "Action history [" + "status=" + actionStatus + ", messages={" + messages.toString() + "}]";
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Download information for all artifacts related to a specific {@link DdiChunk}
* .
*/
public class DdiArtifact extends ResourceSupport {
@NotNull
@JsonProperty
private String filename;
@JsonProperty
private DdiArtifactHash hashes;
@JsonProperty
private Long size;
public DdiArtifactHash getHashes() {
return hashes;
}
public void setHashes(final DdiArtifactHash hashes) {
this.hashes = hashes;
}
public String getFilename() {
return filename;
}
public void setFilename(final String fileName) {
filename = fileName;
}
public Long getSize() {
return size;
}
public void setSize(final Long size) {
this.size = size;
}
}

View File

@@ -0,0 +1,58 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Hashes for given Artifact.
*
*
*/
public class DdiArtifactHash {
@JsonProperty
private String sha1;
@JsonProperty
private String md5;
/**
* Default constructor.
*/
public DdiArtifactHash() {
// needed for json create
}
/**
* Public constructor.
*
* @param sha1
* @param md5
*/
public DdiArtifactHash(final String sha1, final String md5) {
this.sha1 = sha1;
this.md5 = md5;
}
/**
* @return the sha1
*/
public String getSha1() {
return sha1;
}
/**
* @return the md5
*/
public String getMd5() {
return md5;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
/**
* Cancel action to be provided to the target.
*/
public class DdiCancel {
private final String id;
@NotNull
private final DdiCancelActionToStop cancelAction;
/**
* Parameterized constructor.
*
* @param id
* of the cancel action
* @param cancelAction
* the action
*/
public DdiCancel(final String id, final DdiCancelActionToStop cancelAction) {
this.id = id;
this.cancelAction = cancelAction;
}
public String getId() {
return id;
}
public DdiCancelActionToStop getCancelAction() {
return cancelAction;
}
@Override
public String toString() {
return "Cancel [id=" + id + ", cancelAction=" + cancelAction + "]";
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
/**
* The action that has to be stopped by the target.
*/
public class DdiCancelActionToStop {
@NotNull
private final String stopId;
/**
* Parameterized constructor.
*
* @param stopId
* ID of the action to be stoppedW
*/
public DdiCancelActionToStop(final String stopId) {
this.stopId = stopId;
}
public String getStopId() {
return stopId;
}
@Override
public String toString() {
return "CancelAction [stopId=" + stopId + "]";
}
}

View File

@@ -0,0 +1,94 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.Collections;
import java.util.List;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Deployment chunks.
*/
public class DdiChunk {
@JsonProperty("part")
@NotNull
private String part;
@JsonProperty("version")
@NotNull
private String version;
@JsonProperty("name")
@NotNull
private String name;
@JsonProperty("artifacts")
private List<DdiArtifact> artifacts;
@JsonProperty("metadata")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<DdiMetadata> metadata;
public DdiChunk() {
// needed for json create
}
/**
* Constructor.
*
* @param part
* of the deployment chunk
* @param version
* of the artifact
* @param name
* of the artifact
* @param artifacts
* download information
* @param metadata
* optional as additional information for the target/device
*/
public DdiChunk(final String part, final String version, final String name, final List<DdiArtifact> artifacts,
final List<DdiMetadata> metadata) {
this.part = part;
this.version = version;
this.name = name;
this.artifacts = artifacts;
this.metadata = metadata;
}
public String getPart() {
return part;
}
public String getVersion() {
return version;
}
public String getName() {
return name;
}
public List<DdiArtifact> getArtifacts() {
if (artifacts == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(artifacts);
}
public List<DdiMetadata> getMetadata() {
return metadata;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Standard configuration for the target.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DdiConfig {
@JsonProperty
private DdiPolling polling;
/**
* Constructor.
*
* @param polling
* configuration of the SP target
*/
public DdiConfig(final DdiPolling polling) {
this.polling = polling;
}
/**
* Constructor.
*/
public DdiConfig() {
// needed for json create.
}
public DdiPolling getPolling() {
return polling;
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.Map;
import javax.validation.constraints.NotEmpty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Feedback channel for ConfigData action.
*/
public class DdiConfigData extends DdiActionFeedback {
@NotEmpty
private final Map<String, String> data;
private final DdiUpdateMode mode;
/**
* Constructor.
*
* @param id
* of the actions the feedback is for
* @param time
* of the feedback
* @param status
* is the feedback itself
* @param data
* contains the attributes.
*/
@JsonCreator
public DdiConfigData(@JsonProperty(value = "id") final Long id, @JsonProperty(value = "time") final String time,
@JsonProperty(value = "status") final DdiStatus status,
@JsonProperty(value = "data") final Map<String, String> data,
@JsonProperty(value = "mode") final DdiUpdateMode mode) {
super(id, time, status);
this.data = data;
this.mode = mode;
}
public Map<String, String> getData() {
return data;
}
public DdiUpdateMode getMode() {
return mode;
}
@Override
public String toString() {
return "ConfigData [data=" + data + ", mode=" + mode + ", toString()=" + super.toString() + "]";
}
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* {@link DdiControllerBase} resource content.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DdiControllerBase extends ResourceSupport {
@JsonProperty
private DdiConfig config;
/**
* Constructor.
*
* @param config
* configuration of the SP target
*/
public DdiControllerBase(final DdiConfig config) {
this.config = config;
}
public DdiControllerBase() {
// needed for json create
}
public DdiConfig getConfig() {
return config;
}
}

View File

@@ -0,0 +1,158 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.Collections;
import java.util.List;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Detailed update action information.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DdiDeployment {
private HandlingType download;
private HandlingType update;
@JsonProperty("chunks")
@NotNull
private List<DdiChunk> chunks;
private DdiMaintenanceWindowStatus maintenanceWindow;
/**
* Constructor.
*/
public DdiDeployment() {
// needed for json create.
}
/**
* Constructor.
*
* @param download
* handling type
* @param update
* handling type
* @param chunks
* to handle.
* @param maintenanceWindow
* specifying whether there is a maintenance schedule associated.
* If it is, the value is either 'available' (i.e. the
* maintenance window is now available as per defined schedule
* and the update can progress) or 'unavailable' (implying that
* maintenance window is not available now and update should not
* be attempted). If there is no maintenance schedule defined,
* the parameter is null.
*/
public DdiDeployment(final HandlingType download, final HandlingType update, final List<DdiChunk> chunks,
final DdiMaintenanceWindowStatus maintenanceWindow) {
this.download = download;
this.update = update;
this.chunks = chunks;
this.maintenanceWindow = maintenanceWindow;
}
public HandlingType getDownload() {
return download;
}
public HandlingType getUpdate() {
return update;
}
public List<DdiChunk> getChunks() {
if (chunks == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(chunks);
}
public DdiMaintenanceWindowStatus getMaintenanceWindow() {
return this.maintenanceWindow;
}
/**
* The handling type for the update action.
*/
public enum HandlingType {
/**
* Not necessary for the command.
*/
SKIP("skip"),
/**
* Try to execute (local applications may intervene by SP control API).
*/
ATTEMPT("attempt"),
/**
* Execution independent of local intervention attempts.
*/
FORCED("forced");
private String name;
HandlingType(final String name) {
this.name = name;
}
@JsonValue
public String getName() {
return name;
}
}
/**
* Status of the maintenance window for action.
*/
public enum DdiMaintenanceWindowStatus {
/**
* A window is currently available, target can go ahead with
* installation.
*/
AVAILABLE("available"),
/**
* A window is not available, target should wait and skip the
* installation.
*/
UNAVAILABLE("unavailable");
private String status;
DdiMaintenanceWindowStatus(final String status) {
this.status = status;
}
/**
* @return status of maintenance window.
*/
@JsonValue
public String getStatus() {
return this.status;
}
}
@Override
public String toString() {
return "Deployment [download=" + download + ", update=" + update + ", chunks=" + chunks
+ (maintenanceWindow == null ? "]" : (", maintenanceWindow=" + maintenanceWindow + "]"));
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
/**
* Update action resource.
*/
@JsonPropertyOrder({ "id", "deployment", "actionHistory" })
public class DdiDeploymentBase extends ResourceSupport {
@JsonProperty("id")
@NotNull
private final String deplyomentId;
@JsonProperty("deployment")
@NotNull
private final DdiDeployment deployment;
/**
* Action history containing current action status and a list of feedback
* messages received earlier from the controller.
*/
@JsonProperty("actionHistory")
@JsonInclude(JsonInclude.Include.NON_NULL)
private final DdiActionHistory actionHistory;
/**
* Constructor.
*
* @param id
* of the update action
* @param deployment
* details
* @param actionHistory
* containing current action status and a list of feedback
* messages received earlier from the controller.
*/
@JsonCreator
public DdiDeploymentBase(@JsonProperty("id") final String id,
@JsonProperty("deplyomentId") final DdiDeployment deployment,
@JsonProperty("actionHistory") final DdiActionHistory actionHistory) {
this.deplyomentId = id;
this.deployment = deployment;
this.actionHistory = actionHistory;
}
public DdiDeployment getDeployment() {
return deployment;
}
/**
* Returns the action history containing current action status and a list of
* feedback messages received earlier from the controller.
*
* @return {@link DdiActionHistory}
*/
public DdiActionHistory getActionHistory() {
return actionHistory;
}
@Override
public String toString() {
return "DeploymentBase [id=" + deplyomentId + ", deployment=" + deployment + " actionHistory="
+ actionHistory + "]";
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Additional metadata to be provided for the target/device.
*
*/
public class DdiMetadata {
@JsonProperty
@NotNull
private final String key;
@JsonProperty
@NotNull
private final String value;
public DdiMetadata(final String key, final String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Polling interval for the SP target.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class DdiPolling {
@JsonProperty
private String sleep;
/**
* Constructor.
*
* @param sleep
* between polls
*/
public DdiPolling(final String sleep) {
this.sleep = sleep;
}
/**
* Constructor.
*
*/
public DdiPolling() {
// needed for json create
}
public String getSleep() {
return sleep;
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Action fulfillment progress by means of gives the achieved amount of maximal
* of possible levels.
*/
public class DdiProgress {
@NotNull
private final Integer cnt;
private final Integer of;
/**
* Constructor.
*
* @param cnt
* achieved amount
* @param of
* maximum levels
*/
@JsonCreator
public DdiProgress(@JsonProperty("cnt") final Integer cnt, @JsonProperty("of") final Integer of) {
this.cnt = cnt;
this.of = of;
}
public Integer getCnt() {
return cnt;
}
public Integer getOf() {
return of;
}
@Override
public String toString() {
return "Progress [cnt=" + cnt + ", of=" + of + "]";
}
}

View File

@@ -0,0 +1,90 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Result information of the action progress which can by an intermediate or
* final update.
*/
public class DdiResult {
@NotNull
@Valid
private final FinalResult finished;
private final DdiProgress progress;
/**
* Constructor.
*
* @param finished
* as final result
* @param progress
* if not yet finished
*/
@JsonCreator
public DdiResult(@JsonProperty("finished") final FinalResult finished,
@JsonProperty("progress") final DdiProgress progress) {
this.finished = finished;
this.progress = progress;
}
public FinalResult getFinished() {
return finished;
}
public DdiProgress getProgress() {
return progress;
}
/**
* Defined status of the final result.
*
*/
public enum FinalResult {
/**
* Execution was successful.
*/
SUCESS("success"),
/**
* Execution terminated with errors or without the expected result.
*/
FAILURE("failure"),
/**
* No final result could be determined (yet).
*/
NONE("none");
private String name;
FinalResult(final String name) {
this.name = name;
}
@JsonValue
public String getName() {
return name;
}
}
@Override
public String toString() {
return "Result [finished=" + finished + ", progress=" + progress + "]";
}
}

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import java.util.Collections;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Details status information concerning the action processing.
*/
public class DdiStatus {
@NotNull
@Valid
private final ExecutionStatus execution;
@NotNull
@Valid
private final DdiResult result;
private final List<String> details;
/**
* Constructor.
*
* @param execution
* status
* @param result
* information
* @param details
* as optional addition
*/
@JsonCreator
public DdiStatus(@JsonProperty("execution") final ExecutionStatus execution,
@JsonProperty("result") final DdiResult result, @JsonProperty("details") final List<String> details) {
this.execution = execution;
this.result = result;
this.details = details;
}
public ExecutionStatus getExecution() {
return execution;
}
public DdiResult getResult() {
return result;
}
public List<String> getDetails() {
if (details == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(details);
}
/**
* The element status contains information about the execution of the
* operation.
*
*/
public enum ExecutionStatus {
/**
* Execution of the action has finished.
*/
CLOSED("closed"),
/**
* Execution has started but has not yet finished.
*/
PROCEEDING("proceeding"),
/**
* Execution was suspended from outside.
*/
CANCELED("canceled"),
/**
* Action has been noticed and is intended to run.
*/
SCHEDULED("scheduled"),
/**
* Action was not accepted.
*/
REJECTED("rejected"),
/**
* Action is started after a reset, power loss, etc.
*/
RESUMED("resumed"),
/**
* The action has been downloaded by the target.
*/
DOWNLOADED("downloaded"),
/**
* Target starts to download.
*/
DOWNLOAD("download");
private String name;
ExecutionStatus(final String name) {
this.name = name;
}
@JsonValue
public String getName() {
return name;
}
}
@Override
public String toString() {
return "Status [execution=" + execution + ", result=" + result + ", details=" + details + "]";
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2018 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.json.model;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Enumerates the supported update modes. Each mode represents an attribute
* update strategy.
*
* @see DdiConfigData
*/
public enum DdiUpdateMode {
/**
* Merge update strategy
*/
MERGE("merge"),
/**
* Replacement update strategy
*/
REPLACE("replace"),
/**
* Removal update strategy
*/
REMOVE("remove");
private String name;
DdiUpdateMode(final String name) {
this.name = name;
}
@JsonValue
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.api;
/**
* Constants for the direct device integration rest resources.
*/
public final class DdiRestConstants {
/**
* The base URL mapping of the direct device integration rest resources.
*/
public static final String BASE_V1_REQUEST_MAPPING = "/{tenant}/controller/v1";
/**
* Deployment action resources.
*/
public static final String DEPLOYMENT_BASE_ACTION = "deploymentBase";
/**
* Cancel action resources.
*/
public static final String CANCEL_ACTION = "cancelAction";
/**
* Feedback channel.
*/
public static final String FEEDBACK = "feedback";
/**
* File suffix for MDH hash download (see Linux md5sum).
*/
public static final String ARTIFACT_MD5_DWNL_SUFFIX = ".MD5SUM";
/**
* Config data action resources.
*/
public static final String CONFIG_DATA_ACTION = "configData";
/**
* Default value specifying that no action history to be sent as part of
* response to deploymentBase
* {@link DdiRootControllerRestApi#getControllerBasedeploymentAction}.
*/
public static final String NO_ACTION_HISTORY = "0";
private DdiRestConstants() {
// constant class, private constructor.
}
}

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.api;
import java.io.InputStream;
import java.lang.annotation.Target;
import java.util.List;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiCancel;
import org.eclipse.hawkbit.ddi.json.model.DdiConfigData;
import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* REST resource handling for root controller CRUD operations.
*/
@RequestMapping(DdiRestConstants.BASE_V1_REQUEST_MAPPING)
public interface DdiRootControllerRestApi {
/**
* Returns all artifacts of a given software module and target.
*
* @param tenant
* of the client
* @param controllerId
* of the target that matches to controller id
* @param softwareModuleId
* of the software module
* @return the response
*/
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<DdiArtifact>> getSoftwareModulesArtifacts(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId);
/**
* Root resource for an individual {@link Target}.
*
* @param tenant
* of the request
* @param controllerId
* of the target that matches to controller id
* @param request
* the HTTP request injected by spring
* @return the response
*/
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<DdiControllerBase> getControllerBase(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId);
/**
* Handles GET {@link DdiArtifact} download request. This could be full or
* partial (as specified by RFC7233 (Range Requests)) download request.
*
* @param tenant
* of the request
* @param controllerId
* of the target
* @param softwareModuleId
* of the parent software module
* @param fileName
* of the related local artifact
* @param response
* of the servlet
* @param request
* from the client
*
* @return response of the servlet which in case of success is status code
* {@link HttpStatus#OK} or in case of partial download
* {@link HttpStatus#PARTIAL_CONTENT}.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}")
ResponseEntity<InputStream> downloadArtifact(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName);
/**
* Handles GET {@link DdiArtifact} MD5 checksum file download request.
*
* @param tenant
* of the request
* @param controllerId
* of the target
* @param softwareModuleId
* of the parent software module
* @param fileName
* of the related local artifact
* @param response
* of the servlet
* @param request
* the HTTP request injected by spring
*
* @return {@link ResponseEntity} with status {@link HttpStatus#OK} if
* successful
*/
@RequestMapping(method = RequestMethod.GET, value = "/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{fileName}"
+ DdiRestConstants.ARTIFACT_MD5_DWNL_SUFFIX, produces = MediaType.TEXT_PLAIN_VALUE)
ResponseEntity<Void> downloadArtifactMd5(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName);
/**
* Resource for software module.
*
* @param tenant
* of the request
* @param controllerId
* of the target
* @param actionId
* of the {@link DdiDeploymentBase} that matches to active
* actions.
* @param resource
* an hashcode of the resource which indicates if the action has
* been changed, e.g. from 'soft' to 'force' and the eTag needs
* to be re-generated
* @param actionHistoryMessageCount
* specifies the number of messages to be returned from action
* history. Regardless of the passed value, in order to restrict
* resource utilization by controllers, maximum number of
* messages that are retrieved from database is limited by
* {@link RepositoryConstants#MAX_ACTION_HISTORY_MSG_COUNT}.
* actionHistoryMessageCount < 0, retrieves the maximum allowed
* number of action status messages from history;
* actionHistoryMessageCount = 0, does not retrieve any message;
* and actionHistoryMessageCount > 0, retrieves the specified
* number of messages, limited by maximum allowed number.
* @param request
* the HTTP request injected by spring
* @return the response
*/
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION
+ "/{actionId}", method = RequestMethod.GET, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount);
/**
* This is the feedback channel for the {@link DdiDeploymentBase} action.
*
* @param tenant
* of the client
* @param feedback
* to provide
* @param controllerId
* of the target that matches to controller id
* @param actionId
* of the action we have feedback for
* @param request
* the HTTP request injected by spring
*
* @return the response
*/
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION + "/{actionId}/"
+ DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId);
/**
* This is the feedback channel for the config data action.
*
* @param tenant
* of the client
* @param configData
* as body
* @param controllerId
* to provide data for
* @param request
* the HTTP request injected by spring
*
* @return status of the request
*/
@RequestMapping(value = "/{controllerId}/"
+ DdiRestConstants.CONFIG_DATA_ACTION, method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Void> putConfigData(@Valid final DdiConfigData configData,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId);
/**
* RequestMethod.GET method for the {@link DdiCancel} action.
*
* @param tenant
* of the request
* @param controllerId
* ID of the calling target
* @param actionId
* of the action
* @param request
* the HTTP request injected by spring
*
* @return the {@link DdiCancel} response
*/
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION
+ "/{actionId}", method = RequestMethod.GET, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<DdiCancel> getControllerCancelAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId);
/**
* RequestMethod.POST method receiving the {@link DdiActionFeedback} from
* the target.
*
* @param tenant
* of the client
* @param feedback
* the {@link DdiActionFeedback} from the target.
* @param controllerId
* the ID of the calling target
* @param actionId
* of the action we have feedback for
* @param request
* the HTTP request injected by spring
*
* @return the {@link DdiActionFeedback} response
*/
@RequestMapping(value = "/{controllerId}/" + DdiRestConstants.CANCEL_ACTION + "/{actionId}/"
+ DdiRestConstants.FEEDBACK, method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<Void> postCancelActionFeedback(@Valid final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId);
}

View File

@@ -0,0 +1,13 @@
# Eclipse.IoT hawkBit - DDI Resource
This is the server-side implementation of the hawkBit DDI API and the hawkBit DDI Download API that is used by devices for communicating with the HawkBit Update Server through HTTP.
# Compile
#### Build hawkbit-ddi-resource
```
$ cd hawkbit/hawkbit-ddi-resource
$ mvn clean install
```

View File

@@ -0,0 +1,131 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-parent</artifactId>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-ddi-resource</artifactId>
<name>hawkBit :: REST :: DDI Resources</name>
<dependencies>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-ddi-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.plugin</groupId>
<artifactId>spring-plugin-core</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-test</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-repository-jpa</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-core</artifactId>
<version>${project.version}</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-http-security</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-aspects</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,165 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.api.ApiType;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.api.URLPlaceholder;
import org.eclipse.hawkbit.api.URLPlaceholder.SoftwareData;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifactHash;
import org.eclipse.hawkbit.ddi.json.model.DdiChunk;
import org.eclipse.hawkbit.ddi.json.model.DdiConfig;
import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
import org.eclipse.hawkbit.ddi.json.model.DdiMetadata;
import org.eclipse.hawkbit.ddi.json.model.DdiPolling;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.data.ResponseList;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.http.HttpRequest;
import org.springframework.util.CollectionUtils;
/**
* Utility class for the DDI API.
*/
public final class DataConversionHelper {
// utility class, private constructor.
private DataConversionHelper() {
}
static List<DdiChunk> createChunks(final Target target, final Action uAction,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request, final ControllerManagement controllerManagement) {
final Map<Long, List<SoftwareModuleMetadata>> metadata = controllerManagement
.findTargetVisibleMetaDataBySoftwareModuleId(uAction.getDistributionSet().getModules().stream()
.map(SoftwareModule::getId).collect(Collectors.toList()));
return uAction.getDistributionSet().getModules().stream()
.map(module -> new DdiChunk(mapChunkLegacyKeys(module.getType().getKey()), module.getVersion(),
module.getName(),
createArtifacts(target, module, artifactUrlHandler, systemManagement, request),
mapMetadata(metadata.get(module.getId()))))
.collect(Collectors.toList());
}
private static List<DdiMetadata> mapMetadata(final List<SoftwareModuleMetadata> metadata) {
return CollectionUtils.isEmpty(metadata) ? null
: metadata.stream().map(md -> new DdiMetadata(md.getKey(), md.getValue())).collect(Collectors.toList());
}
private static String mapChunkLegacyKeys(final String key) {
if ("application".equals(key)) {
return "bApp";
}
if ("runtime".equals(key)) {
return "jvm";
}
return key;
}
static List<DdiArtifact> createArtifacts(final Target target, final SoftwareModule module,
final ArtifactUrlHandler artifactUrlHandler, final SystemManagement systemManagement,
final HttpRequest request) {
return new ResponseList<>(module.getArtifacts().stream()
.map(artifact -> createArtifact(target, artifactUrlHandler, artifact, systemManagement, request))
.collect(Collectors.toList()));
}
private static DdiArtifact createArtifact(final Target target, final ArtifactUrlHandler artifactUrlHandler,
final Artifact artifact, final SystemManagement systemManagement, final HttpRequest request) {
final DdiArtifact file = new DdiArtifact();
file.setHashes(new DdiArtifactHash(artifact.getSha1Hash(), artifact.getMd5Hash()));
file.setFilename(artifact.getFilename());
file.setSize(artifact.getSize());
artifactUrlHandler
.getUrls(new URLPlaceholder(systemManagement.getTenantMetadata().getTenant(),
systemManagement.getTenantMetadata().getId(), target.getControllerId(), target.getId(),
new SoftwareData(artifact.getSoftwareModule().getId(), artifact.getFilename(), artifact.getId(),
artifact.getSha1Hash())),
ApiType.DDI, request.getURI())
.forEach(entry -> file.add(new Link(entry.getRef()).withRel(entry.getRel())));
return file;
}
static DdiControllerBase fromTarget(final Target target, final Action action,
final String defaultControllerPollTime, final TenantAware tenantAware) {
final DdiControllerBase result = new DdiControllerBase(
new DdiConfig(new DdiPolling(defaultControllerPollTime)));
if (action != null) {
if (action.isCancelingOrCanceled()) {
result.add(ControllerLinkBuilder
.linkTo(ControllerLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerCancelAction(tenantAware.getCurrentTenant(), target.getControllerId(),
action.getId()))
.withRel(DdiRestConstants.CANCEL_ACTION));
} else {
// we need to add the hashcode here of the actionWithStatus
// because the action might
// have changed from 'soft' to 'forced' type and we need to
// change the payload of the
// response because of eTags.
result.add(ControllerLinkBuilder
.linkTo(ControllerLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.getControllerBasedeploymentAction(tenantAware.getCurrentTenant(),
target.getControllerId(), action.getId(), calculateEtag(action), null))
.withRel(DdiRestConstants.DEPLOYMENT_BASE_ACTION));
}
}
if (target.isRequestControllerAttributes()) {
result.add(ControllerLinkBuilder
.linkTo(ControllerLinkBuilder.methodOn(DdiRootController.class, tenantAware.getCurrentTenant())
.putConfigData(null, tenantAware.getCurrentTenant(), target.getControllerId()))
.withRel(DdiRestConstants.CONFIG_DATA_ACTION));
}
return result;
}
/**
* Calculates an etag for the given {@link Action} based on the entities
* hashcode and the {@link Action#isHitAutoForceTime(long)} to reflect a
* force switch.
*
* @param action
* to calculate the etag for
* @return the etag
*/
private static int calculateEtag(final Action action) {
final int prime = 31;
int result = action.hashCode();
int offsetPrime = action.isHitAutoForceTime(System.currentTimeMillis()) ? 1231 : 1237;
offsetPrime = (action.hasMaintenanceSchedule() && action.isMaintenanceWindowAvailable()) ? 1249 : offsetPrime;
result = prime * result + offsetPrime;
return result;
}
}

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import org.eclipse.hawkbit.rest.RestConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Controller;
/**
* Enable {@link ComponentScan} in the resource package to setup all
* {@link Controller} annotated classes and setup the REST-Resources for the
* Direct Device Integration API.
*/
@Configuration
@ComponentScan
@Import(RestConfiguration.class)
public class DdiApiConfiguration {
}

View File

@@ -0,0 +1,596 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import org.eclipse.hawkbit.api.ArtifactUrlHandler;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiActionFeedback;
import org.eclipse.hawkbit.ddi.json.model.DdiActionHistory;
import org.eclipse.hawkbit.ddi.json.model.DdiArtifact;
import org.eclipse.hawkbit.ddi.json.model.DdiCancel;
import org.eclipse.hawkbit.ddi.json.model.DdiCancelActionToStop;
import org.eclipse.hawkbit.ddi.json.model.DdiChunk;
import org.eclipse.hawkbit.ddi.json.model.DdiConfigData;
import org.eclipse.hawkbit.ddi.json.model.DdiControllerBase;
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment;
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.HandlingType;
import org.eclipse.hawkbit.ddi.json.model.DdiDeployment.DdiMaintenanceWindowStatus;
import org.eclipse.hawkbit.ddi.json.model.DdiDeploymentBase;
import org.eclipse.hawkbit.ddi.json.model.DdiResult.FinalResult;
import org.eclipse.hawkbit.ddi.json.model.DdiUpdateMode;
import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants;
import org.eclipse.hawkbit.ddi.rest.api.DdiRootControllerRestApi;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.RepositoryConstants;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.UpdateMode;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.exception.ArtifactBinaryNotFoundException;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.util.FileStreamingUtil;
import org.eclipse.hawkbit.rest.util.HttpUtil;
import org.eclipse.hawkbit.rest.util.RequestResponseContextHolder;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.util.IpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
/**
* The {@link DdiRootController} of the hawkBit server DDI API that is queried
* by the hawkBit controller in order to pull {@link Action}s that have to be
* fulfilled and report status updates concerning the {@link Action} processing.
*
* Transactional (read-write) as all queries at least update the last poll time.
*/
@RestController
@Scope(value = WebApplicationContext.SCOPE_REQUEST)
public class DdiRootController implements DdiRootControllerRestApi {
private static final Logger LOG = LoggerFactory.getLogger(DdiRootController.class);
private static final String GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET = "given action ({}) is not assigned to given target ({}).";
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ControllerManagement controllerManagement;
@Autowired
private ArtifactManagement artifactManagement;
@Autowired
private HawkbitSecurityProperties securityProperties;
@Autowired
private TenantAware tenantAware;
@Autowired
private SystemManagement systemManagement;
@Autowired
private ArtifactUrlHandler artifactUrlHandler;
@Autowired
private RequestResponseContextHolder requestResponseContextHolder;
@Autowired
private EntityFactory entityFactory;
@Override
public ResponseEntity<List<DdiArtifact>> getSoftwareModulesArtifacts(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId) {
LOG.debug("getSoftwareModulesArtifacts({})", controllerId);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final SoftwareModule softwareModule = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
return new ResponseEntity<>(
DataConversionHelper.createArtifacts(target, softwareModule, artifactUrlHandler, systemManagement,
new ServletServerHttpRequest(requestResponseContextHolder.getHttpServletRequest())),
HttpStatus.OK);
}
@Override
public ResponseEntity<DdiControllerBase> getControllerBase(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId) {
LOG.debug("getControllerBase({})", controllerId);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(controllerId, IpUtil
.getClientIpFromRequest(requestResponseContextHolder.getHttpServletRequest(), securityProperties));
final Action action = controllerManagement.findOldestActiveActionByTarget(controllerId).orElse(null);
checkAndCancelExpiredAction(action);
return new ResponseEntity<>(
DataConversionHelper.fromTarget(target, action,
action == null ? controllerManagement.getPollingTime()
: controllerManagement.getPollingTimeForAction(action.getId()),
tenantAware),
HttpStatus.OK);
}
@Override
public ResponseEntity<InputStream> downloadArtifact(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName) {
final ResponseEntity<InputStream> result;
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
if (checkModule(fileName, module)) {
LOG.warn("Softare module with id {} could not be found.", softwareModuleId);
result = ResponseEntity.notFound().build();
} else {
// Exception squid:S3655 - Optional access is checked in checkModule
// subroutine
@SuppressWarnings("squid:S3655")
final Artifact artifact = module.getArtifactByFilename(fileName).get();
final AbstractDbArtifact file = artifactManagement.loadArtifactBinary(artifact.getSha1Hash())
.orElseThrow(() -> new ArtifactBinaryNotFoundException(artifact.getSha1Hash()));
final String ifMatch = requestResponseContextHolder.getHttpServletRequest().getHeader(HttpHeaders.IF_MATCH);
if (ifMatch != null && !HttpUtil.matchesHttpHeader(ifMatch, artifact.getSha1Hash())) {
result = new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} else {
final ActionStatus action = checkAndLogDownload(requestResponseContextHolder.getHttpServletRequest(),
target, module.getId());
final Long statusId = action.getId();
result = FileStreamingUtil.writeFileResponse(file, artifact.getFilename(), artifact.getCreatedAt(),
requestResponseContextHolder.getHttpServletResponse(),
requestResponseContextHolder.getHttpServletRequest(),
(length, shippedSinceLastEvent, total) -> eventPublisher
.publishEvent(new DownloadProgressEvent(tenantAware.getCurrentTenant(), statusId,
shippedSinceLastEvent, applicationContext.getId())));
}
}
return result;
}
private ActionStatus checkAndLogDownload(final HttpServletRequest request, final Target target, final Long module) {
final Action action = controllerManagement
.getActionForDownloadByTargetAndSoftwareModule(target.getControllerId(), module)
.orElseThrow(() -> new SoftwareModuleNotAssignedToTargetException(module, target.getControllerId()));
final String range = request.getHeader("Range");
String message;
if (range != null) {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads range " + range + " of: "
+ request.getRequestURI();
} else {
message = RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target downloads " + request.getRequestURI();
}
return controllerManagement.addInformationalActionStatus(
entityFactory.actionStatus().create(action.getId()).status(Status.DOWNLOAD).message(message));
}
private static boolean checkModule(final String fileName, final SoftwareModule module) {
return null == module || !module.getArtifactByFilename(fileName).isPresent();
}
@Override
// Exception squid:S3655 - Optional access is checked in checkModule
// subroutine
@SuppressWarnings("squid:S3655")
public ResponseEntity<Void> downloadArtifactMd5(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") final String controllerId,
@PathVariable("softwareModuleId") final Long softwareModuleId,
@PathVariable("fileName") final String fileName) {
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final SoftwareModule module = controllerManagement.getSoftwareModule(softwareModuleId)
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, softwareModuleId));
if (checkModule(fileName, module)) {
LOG.warn("Software module with id {} could not be found.", softwareModuleId);
return ResponseEntity.notFound().build();
}
final Artifact artifact = module.getArtifactByFilename(fileName)
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, fileName));
checkAndLogDownload(requestResponseContextHolder.getHttpServletRequest(), target, module.getId());
try {
FileStreamingUtil.writeMD5FileResponse(requestResponseContextHolder.getHttpServletResponse(),
artifact.getMd5Hash(), fileName);
} catch (final IOException e) {
LOG.error("Failed to stream MD5 File", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<DdiDeploymentBase> getControllerBasedeploymentAction(
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") final Long actionId,
@RequestParam(value = "c", required = false, defaultValue = "-1") final int resource,
@RequestParam(value = "actionHistory", defaultValue = DdiRestConstants.NO_ACTION_HISTORY) final Integer actionHistoryMessageCount) {
LOG.debug("getControllerBasedeploymentAction({},{})", controllerId, resource);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
checkAndCancelExpiredAction(action);
if (!action.isCancelingOrCanceled()) {
final List<DdiChunk> chunks = DataConversionHelper.createChunks(target, action, artifactUrlHandler,
systemManagement,
new ServletServerHttpRequest(requestResponseContextHolder.getHttpServletRequest()),
controllerManagement);
final List<String> actionHistoryMsgs = controllerManagement.getActionHistoryMessages(action.getId(),
actionHistoryMessageCount == null ? Integer.parseInt(DdiRestConstants.NO_ACTION_HISTORY)
: actionHistoryMessageCount);
final DdiActionHistory actionHistory = actionHistoryMsgs.isEmpty() ? null
: new DdiActionHistory(action.getStatus().name(), actionHistoryMsgs);
final HandlingType downloadType = action.isForce() ? HandlingType.FORCED : HandlingType.ATTEMPT;
final HandlingType updateType = calculateUpdateType(action, downloadType);
final DdiMaintenanceWindowStatus maintenanceWindow = calculateMaintenanceWindow(action);
final DdiDeploymentBase base = new DdiDeploymentBase(Long.toString(action.getId()),
new DdiDeployment(downloadType, updateType, chunks, maintenanceWindow), actionHistory);
LOG.debug("Found an active UpdateAction for target {}. returning deyploment: {}", controllerId, base);
controllerManagement.registerRetrieved(action.getId(), RepositoryConstants.SERVER_MESSAGE_PREFIX
+ "Target retrieved update action and should start now the download.");
return new ResponseEntity<>(base, HttpStatus.OK);
}
return ResponseEntity.notFound().build();
}
private static DdiMaintenanceWindowStatus calculateMaintenanceWindow(final Action action) {
if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? DdiMaintenanceWindowStatus.AVAILABLE
: DdiMaintenanceWindowStatus.UNAVAILABLE;
}
return null;
}
private static HandlingType calculateUpdateType(final Action action, final HandlingType downloadType) {
if (action.hasMaintenanceSchedule()) {
return action.isMaintenanceWindowAvailable() ? downloadType : HandlingType.SKIP;
}
return downloadType;
}
@Override
public ResponseEntity<Void> postBasedeploymentActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
if (!actionId.equals(feedback.getId())) {
LOG.warn(
"provideBasedeploymentActionFeedback: action in payload ({}) was not identical to action in path ({}).",
feedback.getId(), actionId);
return ResponseEntity.notFound().build();
}
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
if (!action.isActive()) {
LOG.warn("Updating action {} with feedback {} not possible since action not active anymore.",
action.getId(), feedback.getId());
return new ResponseEntity<>(HttpStatus.GONE);
}
controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, feedback.getId()));
return ResponseEntity.ok().build();
}
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
final Long actionid) {
final List<String> messages = new ArrayList<>();
if (!CollectionUtils.isEmpty(feedback.getStatus().getDetails())) {
messages.addAll(feedback.getStatus().getDetails());
}
Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
LOG.debug("Controller confirmed cancel (actionid: {}, controllerId: {}) as we got {} report.", actionid,
controllerId, feedback.getStatus().getExecution());
status = Status.CANCELED;
addMessageIfEmpty("Target confirmed cancelation.", messages);
break;
case REJECTED:
LOG.info("Controller reported internal error (actionid: {}, controllerId: {}) as we got {} report.",
actionid, controllerId, feedback.getStatus().getExecution());
status = Status.WARNING;
addMessageIfEmpty("Target REJECTED update", messages);
break;
case CLOSED:
status = handleClosedCase(feedback, controllerId, actionid, messages);
break;
case DOWNLOAD:
LOG.debug("Controller confirmed status of download (actionId: {}, controllerId: {}) as we got {} report.",
actionid, controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOAD;
addMessageIfEmpty("Target confirmed download start", messages);
break;
case DOWNLOADED:
LOG.debug("Controller confirmed download (actionId: {}, controllerId: {}) as we got {} report.", actionid,
controllerId, feedback.getStatus().getExecution());
status = Status.DOWNLOADED;
addMessageIfEmpty("Target confirmed download finished", messages);
break;
default:
status = handleDefaultCase(feedback, controllerId, actionid, messages);
break;
}
return entityFactory.actionStatus().create(actionid).status(status).messages(messages);
}
private static void addMessageIfEmpty(final String text, final List<String> messages) {
if (messages != null && messages.isEmpty()) {
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + text + ".");
}
}
private Status handleDefaultCase(final DdiActionFeedback feedback, final String controllerId, final Long actionid,
final List<String> messages) {
Status status;
LOG.debug("Controller reported intermediate status (actionid: {}, controllerId: {}) as we got {} report.",
actionid, controllerId, feedback.getStatus().getExecution());
status = Status.RUNNING;
addMessageIfEmpty("Target reported " + feedback.getStatus().getExecution(), messages);
return status;
}
private Status handleClosedCase(final DdiActionFeedback feedback, final String controllerId, final Long actionid,
final List<String> messages) {
Status status;
LOG.debug("Controller reported closed (actionid: {}, controllerId: {}) as we got {} report.", actionid,
controllerId, feedback.getStatus().getExecution());
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
status = Status.ERROR;
addMessageIfEmpty("Target reported CLOSED with ERROR!", messages);
} else {
status = Status.FINISHED;
addMessageIfEmpty("Target reported CLOSED with OK!", messages);
}
return status;
}
@Override
public ResponseEntity<Void> putConfigData(@Valid @RequestBody final DdiConfigData configData,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId) {
controllerManagement.updateControllerAttributes(controllerId, configData.getData(), getUpdateMode(configData));
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<DdiCancel> getControllerCancelAction(@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("getControllerCancelAction({})", controllerId);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
if (action.isCancelingOrCanceled()) {
final DdiCancel cancel = new DdiCancel(String.valueOf(action.getId()),
new DdiCancelActionToStop(String.valueOf(action.getId())));
LOG.debug("Found an active CancelAction for target {}. returning cancel: {}", controllerId, cancel);
controllerManagement.registerRetrieved(action.getId(), RepositoryConstants.SERVER_MESSAGE_PREFIX
+ "Target retrieved cancel action and should start now the cancelation.");
return new ResponseEntity<>(cancel, HttpStatus.OK);
}
return ResponseEntity.notFound().build();
}
@Override
public ResponseEntity<Void> postCancelActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant,
@PathVariable("controllerId") @NotEmpty final String controllerId,
@PathVariable("actionId") @NotEmpty final Long actionId) {
LOG.debug("provideCancelActionFeedback for target [{}]: {}", controllerId, feedback);
final Target target = controllerManagement.getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
if (!actionId.equals(feedback.getId())) {
LOG.warn(
"provideBasedeploymentActionFeedback: action in payload ({}) was not identical to action in path ({}).",
feedback.getId(), actionId);
return ResponseEntity.notFound().build();
}
final Action action = findActionWithExceptionIfNotFound(actionId);
if (!action.getTarget().getId().equals(target.getId())) {
LOG.warn(GIVEN_ACTION_IS_NOT_ASSIGNED_TO_GIVEN_TARGET, action.getId(), target.getId());
return ResponseEntity.notFound().build();
}
controllerManagement
.addCancelActionStatus(generateActionCancelStatus(feedback, target, feedback.getId(), entityFactory));
return ResponseEntity.ok().build();
}
private static ActionStatusCreate generateActionCancelStatus(final DdiActionFeedback feedback, final Target target,
final Long actionid, final EntityFactory entityFactory) {
final List<String> messages = new ArrayList<>();
Status status;
switch (feedback.getStatus().getExecution()) {
case CANCELED:
status = handleCaseCancelCanceled(feedback, target, actionid, messages);
break;
case REJECTED:
LOG.info("Target rejected the cancelation request (actionid: {}, controllerId: {}).", actionid,
target.getControllerId());
status = Status.CANCEL_REJECTED;
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Target rejected the cancelation request.");
break;
case CLOSED:
status = handleCancelClosedCase(feedback, messages);
break;
default:
status = Status.RUNNING;
break;
}
if (feedback.getStatus().getDetails() != null) {
messages.addAll(feedback.getStatus().getDetails());
}
return entityFactory.actionStatus().create(actionid).status(status).messages(messages);
}
private static Status handleCancelClosedCase(final DdiActionFeedback feedback, final List<String> messages) {
Status status;
if (feedback.getStatus().getResult().getFinished() == FinalResult.FAILURE) {
status = Status.ERROR;
addMessageIfEmpty("Target was not able to complete cancelation", messages);
} else {
status = Status.CANCELED;
addMessageIfEmpty("Cancelation confirmed", messages);
}
return status;
}
private static Status handleCaseCancelCanceled(final DdiActionFeedback feedback, final Target target,
final Long actionid, final List<String> messages) {
Status status;
LOG.error(
"Target reported cancel for a cancel which is not supported by the server (actionid: {}, controllerId: {}) as we got {} report.",
actionid, target.getControllerId(), feedback.getStatus().getExecution());
status = Status.WARNING;
messages.add(RepositoryConstants.SERVER_MESSAGE_PREFIX
+ "Target reported cancel for a cancel which is not supported by the server.");
return status;
}
private Action findActionWithExceptionIfNotFound(final Long actionId) {
return controllerManagement.findActionWithDetails(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
}
/**
* If the action has a maintenance schedule defined but is no longer valid,
* cancel the action.
*
* @param action
* is the {@link Action} to check.
*/
private void checkAndCancelExpiredAction(final Action action) {
if (action != null && action.hasMaintenanceSchedule() && action.isMaintenanceScheduleLapsed()) {
try {
controllerManagement.cancelAction(action.getId());
} catch (final CancelActionNotAllowedException e) {
LOG.info("Cancel action not allowed exception :{}", e);
}
}
}
/**
* Retrieve the update mode from the given update message.
*/
private static UpdateMode getUpdateMode(final DdiConfigData configData) {
final DdiUpdateMode mode = configData.getMode();
if (mode != null) {
return UpdateMode.valueOf(mode.name());
}
return null;
}
}

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import org.eclipse.hawkbit.rest.AbstractRestIntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.TestPropertySource;
@SpringApplicationConfiguration(classes = { DdiApiConfiguration.class })
@TestPropertySource(locations = "classpath:/ddi-test.properties")
public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrationTest {
}

View File

@@ -0,0 +1,368 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang3.RandomUtils;
import org.eclipse.hawkbit.ddi.rest.resource.DdiArtifactDownloadTest.DownloadTestConfiguration;
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.Before;
import org.junit.Test;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import com.google.common.base.Charsets;
import com.google.common.net.HttpHeaders;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test artifact downloads from the controller.
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Artifact Download Resource")
@SpringApplicationConfiguration(classes = DownloadTestConfiguration.class)
public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
private static volatile int downLoadProgress = 0;
private static volatile long shippedBytes = 0;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
@Before
public void setup() {
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}
@Test
@Description("Tests non allowed requests on the artifact ressource, e.g. invalid URI, wrong if-match, wrong command.")
public void invalidRequestsOnArtifactResource() throws Exception {
// create target
final Target target = testdataFactory.createTarget();
final List<Target> targets = Arrays.asList(target);
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds, targets);
// create artifact
final int artifactSize = 5 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
// no artifact available
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455",
target.getControllerId(), getOsModule(ds))).andExpect(status().isNotFound());
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455.MD5SUM",
target.getControllerId(), getOsModule(ds))).andExpect(status().isNotFound());
// SM does not exist
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/1234567890/artifacts/{filename}",
target.getControllerId(), artifact.getFilename())).andExpect(status().isNotFound());
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/1234567890/artifacts/{filename}.MD5SUM",
target.getControllerId(), artifact.getFilename())).andExpect(status().isNotFound());
// test now consistent data to test allowed methods
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header(HttpHeaders.IF_MATCH, artifact.getSha1Hash()))
.andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk());
// test failed If-match
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header(HttpHeaders.IF_MATCH, "fsjkhgjfdhg"))
.andExpect(status().isPreconditionFailed());
// test invalid range
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=1-10,hdsfjksdh"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
mvc.perform(get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename())
.header("Range", "bytes=100-10"))
.andExpect(header().string("Content-Range", "bytes */" + 5 * 1024))
.andExpect(status().isRequestedRangeNotSatisfiable());
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
mvc.perform(post("/{tenant}/controller/v1/{controllerId}/softwaremodules/{smId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isMethodNotAllowed());
}
@Test
@WithUser(principal = "4712", authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Tests valid downloads through the artifact resource by identifying the artifact not by ID but file name.")
public void downloadArtifactThroughFileName() throws Exception {
downLoadProgress = 1;
shippedBytes = 0;
assertThat(softwareModuleManagement.findAll(PAGE)).hasSize(0);
// create target
final Target target = testdataFactory.createTarget();
final List<Target> targets = Arrays.asList(target);
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
// create artifact
final int artifactSize = 5 * 1024 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
// download fails as artifact is not yet assigned
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
target.getControllerId(), getOsModule(ds), artifact.getFilename())).andExpect(status().isNotFound());
// now assign and download successful
assignDistributionSet(ds, targets);
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().string("Content-Disposition", "attachment;filename=" + artifact.getFilename()))
.andReturn();
assertTrue("The same file that was uploaded is expected when downloaded",
Arrays.equals(result.getResponse().getContentAsByteArray(), random));
// download complete
assertThat(downLoadProgress).isEqualTo(10);
assertThat(shippedBytes).isEqualTo(artifactSize);
}
@Test
@Description("Tests valid MD5SUm file downloads through the artifact resource by identifying the artifact by ID.")
public void downloadMd5sumThroughControllerApi() throws Exception {
// create target
final Target target = testdataFactory.createTarget();
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds, target);
// create artifact
final int artifactSize = 5 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
false, artifactSize);
// download
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
"attachment;filename=" + artifact.getFilename() + ".MD5SUM"))
.andReturn();
assertThat(result.getResponse().getContentAsByteArray())
.isEqualTo((artifact.getMd5Hash() + " " + artifact.getFilename()).getBytes(Charsets.US_ASCII));
}
@Test
@WithUser(principal = TestdataFactory.DEFAULT_CONTROLLER_ID, authorities = "ROLE_CONTROLLER", allSpPermissions = true)
@Description("Test various HTTP range requests for artifact download, e.g. chunk download or download resume.")
public void rangeDownloadArtifact() throws Exception {
// create target
final Target target = testdataFactory.createTarget();
final List<Target> targets = Arrays.asList(target);
// create ds
final DistributionSet ds = testdataFactory.createDistributionSet("");
final int resultLength = 5 * 1000 * 1024;
// create artifact
final byte random[] = RandomUtils.nextBytes(resultLength);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
false, resultLength);
assertThat(random.length).isEqualTo(resultLength);
// now assign and download successful
assignDistributionSet(ds, targets);
final int range = 100 * 1024;
// full file download with standard range request
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (int i = 0; i < resultLength / range; i++) {
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
final MvcResult result = mvc.perform(get(
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
"bytes=" + rangeString))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().longValue("Content-Length", range))
.andExpect(header().string("Content-Range", "bytes " + rangeString + "/" + resultLength))
.andExpect(header().string("Content-Disposition", "attachment;filename=file1")).andReturn();
outputStream.write(result.getResponse().getContentAsByteArray());
}
assertThat(outputStream.toByteArray()).isEqualTo(random);
// return last 1000 Bytes
MvcResult result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=-1000"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().longValue("Content-Length", 1000))
.andExpect(header().string("Content-Range",
"bytes " + (resultLength - 1000) + "-" + (resultLength - 1) + "/" + resultLength))
.andExpect(header().string("Content-Disposition", "attachment;filename=file1")).andReturn();
assertThat(result.getResponse().getContentAsByteArray())
.isEqualTo(Arrays.copyOfRange(random, resultLength - 1000, resultLength));
// skip first 1000 Bytes and return the rest
result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=1000-"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().longValue("Content-Length", resultLength - 1000))
.andExpect(header().string("Content-Range",
"bytes " + 1000 + "-" + (resultLength - 1) + "/" + resultLength))
.andExpect(header().string("Content-Disposition", "attachment;filename=file1")).andReturn();
assertThat(result.getResponse().getContentAsByteArray())
.isEqualTo(Arrays.copyOfRange(random, 1000, resultLength));
// Start download from file end fails
mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=" + random.length + "-"))
.andExpect(status().isRequestedRangeNotSatisfiable())
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().string("Content-Range", "bytes */" + random.length))
.andExpect(header().string("Content-Disposition", "attachment;filename=file1"));
// multipart download - first 20 bytes in 2 parts
result = mvc.perform(
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
.header("Range", "bytes=0-9,10-19"))
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))
.andExpect(header().string("Accept-Ranges", "bytes"))
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
.andExpect(header().string("Content-Disposition", "attachment;filename=file1")).andReturn();
outputStream.reset();
outputStream.write("\r\n--THIS_STRING_SEPARATES_MULTIPART\r\n".getBytes(StandardCharsets.ISO_8859_1));
outputStream.write(("Content-Range: bytes 0-9/" + resultLength + "\r\n").getBytes(StandardCharsets.ISO_8859_1));
outputStream.write(Arrays.copyOfRange(random, 0, 10));
outputStream.write("\r\n--THIS_STRING_SEPARATES_MULTIPART\r\n".getBytes(StandardCharsets.ISO_8859_1));
outputStream
.write(("Content-Range: bytes 10-19/" + resultLength + "\r\n").getBytes(StandardCharsets.ISO_8859_1));
outputStream.write(Arrays.copyOfRange(random, 10, 20));
outputStream.write("\r\n--THIS_STRING_SEPARATES_MULTIPART--".getBytes(StandardCharsets.ISO_8859_1));
assertThat(result.getResponse().getContentAsByteArray()).isEqualTo(outputStream.toByteArray());
}
@Configuration
public static class DownloadTestConfiguration {
@Bean
public Listener cancelEventHandlerStubBean() {
return new Listener();
}
}
private static class Listener {
@EventListener(classes = DownloadProgressEvent.class)
public static void listen(final DownloadProgressEvent event) {
downLoadProgress++;
shippedBytes += event.getShippedBytesSinceLast();
}
}
}

View File

@@ -0,0 +1,516 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.Test;
import org.springframework.http.MediaType;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test cancel action from the controller.
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Cancel Action Resource")
public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("Test of the controller can continue a started update even after a cancel command if it so desires.")
public void rootRsCancelActionButContinueAnyway() throws Exception {
// prepare test data
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget();
final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActions().get(0);
final Action cancelAction = deploymentManagement.cancelAction(actionId);
// controller rejects cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "rejected"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
final long current = System.currentTimeMillis();
// get update action anyway
mvc.perform(
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId,
tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(actionId))))
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())));
// and finish it
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ actionId + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(actionId.toString(), "closed", "success"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// check database after test
assertThat(deploymentManagement.getAssignedDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
.isEqualTo(ds);
assertThat(deploymentManagement.getInstalledDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
.isEqualTo(ds);
assertThat(
targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getInstallationDate())
.isGreaterThanOrEqualTo(current);
}
@Test
@Description("Test for cancel operation of a update action.")
public void rootRsCancelAction() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget();
final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActions().get(0);
long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID).accept(APPLICATION_JSON_HAL_UTF))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId)));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
// Retrieved is reported
List<Action> activeActionsByTarget = deploymentManagement
.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()).getContent();
assertThat(activeActionsByTarget).hasSize(1);
assertThat(activeActionsByTarget.get(0).getStatus()).isEqualTo(Status.RUNNING);
final Action cancelAction = deploymentManagement.cancelAction(actionId);
activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent();
// the canceled action should still be active!
assertThat(cancelAction.isActive()).isTrue();
assertThat(activeActionsByTarget).hasSize(1);
assertThat(activeActionsByTarget.get(0).getStatus()).isEqualTo(Status.CANCELING);
current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
equalTo("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + cancelAction.getId())));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
current = System.currentTimeMillis();
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
.andExpect(jsonPath("$.cancelAction.stopId", equalTo(String.valueOf(actionId))));
assertThat(targetManagement.getByControllerID(TestdataFactory.DEFAULT_CONTROLLER_ID).get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
// controller confirmed cancelled action, should not be active anymore
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON)
.content(JsonBuilder.cancelActionFeedback(actionId.toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
activeActionsByTarget = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent();
assertThat(activeActionsByTarget).hasSize(0);
final Action canceledAction = deploymentManagement.findAction(cancelAction.getId()).get();
assertThat(canceledAction.isActive()).isFalse();
assertThat(canceledAction.getStatus()).isEqualTo(Status.CANCELED);
}
@Test
@Description("Tests various bad requests and if the server handles them as expected.")
public void badCancelAction() throws Exception {
// not allowed methods
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/1",
tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
// non existing target
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
createCancelAction("34534543");
// wrong media type
mvc.perform(get("/{tenant}/controller/v1/34534543/cancelAction/1", tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
private Action createCancelAction(final String targetid) {
final DistributionSet ds = testdataFactory.createDistributionSet(targetid);
final Target savedTarget = testdataFactory.createTarget(targetid);
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget);
final Long actionId = assignDistributionSet(ds, toAssign).getActions().get(0);
return deploymentManagement.cancelAction(actionId);
}
@Test
@Description("Tests the feedback channel of the cancel operation.")
public void rootRsCancelActionFeedback() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget();
final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions()
.get(0);
// cancel action manually
final Action cancelAction = deploymentManagement.cancelAction(actionId);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(2);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "proceeding"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(3);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "resumed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(4);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "scheduled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(5);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
// cancellation canceled -> should remove the action from active
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "canceled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
// cancellation rejected -> action still active until controller close
// it
// with finished or
// error
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "rejected"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
// update closed -> should remove the action from active
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(8);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(0);
}
@Test
@Description("Tests the feeback chanel of for multiple open cancel operations on the same target.")
public void multipleCancelActionFeedback() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final DistributionSet ds3 = testdataFactory.createDistributionSet("3", true);
final Target savedTarget = testdataFactory.createTarget();
final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions()
.get(0);
final Long actionId2 = assignDistributionSet(ds2.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions()
.get(0);
final Long actionId3 = assignDistributionSet(ds3.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions()
.get(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(3);
// 3 update actions, 0 cancel actions
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(3);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(3);
final Action cancelAction = deploymentManagement.cancelAction(actionId);
final Action cancelAction2 = deploymentManagement.cancelAction(actionId2);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(3);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction.getId()))))
.andExpect(jsonPath("$.cancelAction.stopId", equalTo(String.valueOf(actionId))));
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6);
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
equalTo("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + cancelAction.getId())));
// now lets return feedback for the first cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
// 1 update actions, 1 cancel actions
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction2.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction2.getId()))))
.andExpect(jsonPath("$.cancelAction.stopId", equalTo(String.valueOf(actionId2))));
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(8);
mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(),
TestdataFactory.DEFAULT_CONTROLLER_ID)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.cancelAction.href",
equalTo("http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/"
+ TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + cancelAction2.getId())));
// now lets return feedback for the second cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction2.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction2.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(9);
assertThat(deploymentManagement.getAssignedDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
.isEqualTo(ds3);
mvc.perform(
get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/deploymentBase/" + actionId3,
tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(10);
// 1 update actions, 0 cancel actions
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action cancelAction3 = deploymentManagement.cancelAction(actionId3);
// action is in cancelling state
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
assertThat(deploymentManagement.getAssignedDistributionSet(TestdataFactory.DEFAULT_CONTROLLER_ID).get())
.isEqualTo(ds3);
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction3.getId(), tenantAware.getCurrentTenant()).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(cancelAction3.getId()))))
.andExpect(jsonPath("$.cancelAction.stopId", equalTo(String.valueOf(actionId3))));
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(12);
// now lets return feedback for the third cancelation
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction3.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction3.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(13);
// final status
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(0);
assertThat(deploymentManagement.countActionsByTarget(savedTarget.getControllerId())).isEqualTo(3);
}
@Test
@Description("Tests the feeback channel closing for too many feedbacks, i.e. denial of service prevention.")
public void tooMuchCancelActionFeedback() throws Exception {
testdataFactory.createTarget();
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions()
.get(0);
final Action cancelAction = deploymentManagement.cancelAction(actionId);
final String feedback = JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "proceeding");
// assignDistributionSet creates an ActionStatus and cancel action
// stores an action status, so
// only 97 action status left
for (int i = 0; i < 98; i++) {
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()).content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden());
}
@Test
@Description("test the correct rejection of various invalid feedback requests")
public void badCancelActionFeedback() throws Exception {
final Action cancelAction = createCancelAction(TestdataFactory.DEFAULT_CONTROLLER_ID);
final Action cancelAction2 = createCancelAction("4715");
// not allowed methods
mvc.perform(put("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// bad content type
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_ATOM_XML).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
// bad body
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "546456456"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
// non existing target
mvc.perform(post("/{tenant}/controller/v1/12345/cancelAction/" + cancelAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// invalid action
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback("sdfsdfsdfs", "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback("1234", "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// right action but for wrong target
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction2.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// finally get it right :)
mvc.perform(post("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/"
+ cancelAction.getId() + "/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.cancelActionFeedback(cancelAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,309 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.exception.SpServerError;
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.Test;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import com.google.common.collect.Maps;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test config data from the controller.
*/
@ActiveProfiles({ "im", "test" })
@Features("Component Tests - Direct Device Integration API")
@Stories("Config Data Resource")
public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "are requested only once from the device.")
@SuppressWarnings("squid:S2925")
public void requestConfigDataIfEmpty() throws Exception {
final Target savedTarget = testdataFactory.createTarget("4712");
final long current = System.currentTimeMillis();
mvc.perform(
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(APPLICATION_JSON_HAL_UTF))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.configData.href", equalTo(
"http://localhost/" + tenantAware.getCurrentTenant() + "/controller/v1/4712/configData")));
Thread.sleep(1); // is required: otherwise processing the next line is
// often too fast and
// the following assert will fail
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
final Map<String, String> attributes = Maps.newHashMapWithExpectedSize(1);
attributes.put("dsafsdf", "sdsds");
final Target updateControllerAttributes = controllerManagement
.updateControllerAttributes(savedTarget.getControllerId(), attributes, null);
// request controller attributes need to be false because we don't want
// to request the controller attributes again
assertThat(updateControllerAttributes.isRequestControllerAttributes()).isFalse();
mvc.perform(
get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()).accept(APPLICATION_JSON_HAL_UTF))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.configData.href").doesNotExist());
}
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "can be uploaded correctly by the controller.")
public void putConfigData() throws Exception {
testdataFactory.createTarget("4717");
// initial
final Map<String, String> attributes = new HashMap<>();
attributes.put("dsafsdf", "sdsds");
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes("4717")).isEqualTo(attributes);
// update
attributes.put("sdsds", "123412");
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
assertThat(targetManagement.getControllerAttributes("4717")).isEqualTo(attributes);
}
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "upload quota is enforced to protect the server from malicious attempts.")
public void putTooMuchConfigData() throws Exception {
testdataFactory.createTarget("4717");
// initial
Map<String, String> attributes = new HashMap<>();
for (int i = 0; i < quotaManagement.getMaxAttributeEntriesPerTarget(); i++) {
attributes.put("dsafsdf" + i, "sdsds" + i);
}
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
attributes = new HashMap<>();
attributes.put("on too many", "sdsds");
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isForbidden())
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
}
@Test
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
+ "resource behaves as exptected in cae of invalid request attempts.")
public void badConfigData() throws Exception {
testdataFactory.createTarget("4712");
// not allowed methods
mvc.perform(post("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).//
andExpect(status().isMethodNotAllowed());
mvc.perform(get("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// bad content type
final Map<String, String> attributes = new HashMap<>();
attributes.put("dsafsdf", "sdsds");
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaTypes.HAL_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isUnsupportedMediaType());
// non existing target
mvc.perform(put("/{tenant}/controller/v1/456456/configData", tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// bad body
mvc.perform(put("/{tenant}/controller/v1/4712/configData", tenantAware.getCurrentTenant())
.content("{\"id\": \"51659181\"}").contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@Test
@Description("Verify that config data (device attributes) can be updated by the controller using different update modes (merge, replace, remove).")
public void putConfigDataWithDifferentUpdateModes() throws Exception {
// create a target
final String controllerId = "4717";
testdataFactory.createTarget(controllerId);
final String configDataPath = "/{tenant}/controller/v1/" + controllerId + "/configData";
// no update mode
putConfigDataWithoutUpdateMode(controllerId, configDataPath);
// update mode REPLACE
putConfigDataWithUpdateModeReplace(controllerId, configDataPath);
// update mode MERGE
putConfigDataWithUpdateModeMerge(controllerId, configDataPath);
// update mode REMOVE
putConfigDataWithUpdateModeRemove(controllerId, configDataPath);
// invalid update mode
putConfigDataWithInvalidUpdateMode(configDataPath);
}
@Step
private void putConfigDataWithInvalidUpdateMode(final String configDataPath) throws Exception {
// create some attriutes
final Map<String, String> attributes = new HashMap<>();
attributes.put("k0", "v0");
attributes.put("k1", "v1");
// use an invalid update mode
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed", "KJHGKJHGKJHG"))
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isBadRequest());
}
@Step
private void putConfigDataWithUpdateModeRemove(final String controllerId, final String configDataPath)
throws Exception {
// get the current attributes
final int previousSize = targetManagement.getControllerAttributes(controllerId).size();
// update the attributes using update mode REMOVE
final Map<String, String> removeAttributes = new HashMap<>();
removeAttributes.put("k1", "foo");
removeAttributes.put("k3", "bar");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", removeAttributes, "closed", "remove"))
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute removal
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(previousSize - 2);
assertThat(updatedAttributes).doesNotContainKeys("k1", "k3");
}
@Step
private void putConfigDataWithUpdateModeMerge(final String controllerId, final String configDataPath)
throws Exception {
// get the current attributes
final Map<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
// update the attributes using update mode MERGE
final Map<String, String> mergeAttributes = new HashMap<>();
mergeAttributes.put("k1", "v1_modified_again");
mergeAttributes.put("k4", "v4");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", mergeAttributes, "closed", "merge"))
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute merge
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(4);
assertThat(updatedAttributes).containsAllEntriesOf(mergeAttributes);
assertThat(updatedAttributes.get("k1")).isEqualTo("v1_modified_again");
attributes.keySet().forEach(assertThat(updatedAttributes)::containsKey);
}
@Step
private void putConfigDataWithUpdateModeReplace(final String controllerId, final String configDataPath)
throws Exception {
// get the current attributes
final Map<String, String> attributes = new HashMap<>(targetManagement.getControllerAttributes(controllerId));
// update the attributes using update mode REPLACE
final Map<String, String> replacementAttributes = new HashMap<>();
replacementAttributes.put("k1", "v1_modified");
replacementAttributes.put("k2", "v2");
replacementAttributes.put("k3", "v3");
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", replacementAttributes, "closed", "replace"))
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk());
// verify attribute replacement
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(replacementAttributes.size());
assertThat(updatedAttributes).containsAllEntriesOf(replacementAttributes);
assertThat(updatedAttributes.get("k1")).isEqualTo("v1_modified");
attributes.entrySet().forEach(assertThat(updatedAttributes)::doesNotContain);
}
@Step
private void putConfigDataWithoutUpdateMode(final String controllerId, final String configDataPath)
throws Exception {
// create some attriutes
final Map<String, String> attributes = new HashMap<>();
attributes.put("k0", "v0");
attributes.put("k1", "v1");
// set the initial attributes
mvc.perform(put(configDataPath, tenantAware.getCurrentTenant())
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify the initial parameters
final Map<String, String> updatedAttributes = targetManagement.getControllerAttributes(controllerId);
assertThat(updatedAttributes.size()).isEqualTo(attributes.size());
assertThat(updatedAttributes).containsAllEntriesOf(attributes);
}
}

View File

@@ -0,0 +1,985 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.RandomUtils;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import com.jayway.jsonpath.JsonPath;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test deployment base from the controller.
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Deployment Action Resource")
public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
private static final String HTTP_LOCALHOST = "http://localhost:8080/";
@Test
@Description("Ensures that artifacts are not found, when softare module does not exists.")
public void artifactsNotFound() throws Exception {
final Target target = testdataFactory.createTarget();
final Long softwareModuleIdNotExist = 1l;
mvc.perform(get("/{tenant}/controller/v1/{targetNotExist}/softwaremodules/{softwareModuleId}/artifacts",
tenantAware.getCurrentTenant(), target.getName(), softwareModuleIdNotExist))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
}
@Test
@Description("Ensures that artifacts are found, when software module exists.")
public void artifactsExists() throws Exception {
final Target target = testdataFactory.createTarget();
final DistributionSet distributionSet = testdataFactory.createDistributionSet("");
assignDistributionSet(distributionSet.getId(), target.getName());
final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId();
mvc.perform(get("/{tenant}/controller/v1/{targetNotExist}/softwaremodules/{softwareModuleId}/artifacts",
tenantAware.getCurrentTenant(), target.getName(), softwareModuleId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(0)));
testdataFactory.createArtifacts(softwareModuleId);
mvc.perform(get("/{tenant}/controller/v1/{targetNotExist}/softwaremodules/{softwareModuleId}/artifacts",
tenantAware.getCurrentTenant(), target.getName(), softwareModuleId)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(3)))
.andExpect(jsonPath("$.[?(@.filename==filename0)]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename==filename1)]", hasSize(1)))
.andExpect(jsonPath("$.[?(@.filename==filename2)]", hasSize(1)));
}
@Test
@Description("Forced deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.")
public void deplomentForceAction() throws Exception {
// Prepare test data
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final int artifactSize = 5 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
false, artifactSize);
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
"test1.signature", false, artifactSize);
final Target savedTarget = testdataFactory.createTarget("4712");
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(0);
List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.FORCED,
RepositoryModelConstants.NO_FORCE_TIME, Arrays.asList(savedTarget.getControllerId()))
.getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(deploymentManagement.countActionsAll()).isEqualTo(1);
saved = assignDistributionSet(ds2, saved).getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionsAll()).isEqualTo(2);
final Action uaction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(uaction.getDistributionSet()).isEqualTo(ds);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
// Run test
long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href", startsWith("http://localhost/"
+ tenantAware.getCurrentTenant() + "/controller/v1/4712/deploymentBase/" + uaction.getId())));
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(2);
current = System.currentTimeMillis();
final DistributionSet findDistributionSetByAction = distributionSetManagement.getByAction(action.getId()).get();
mvc.perform(
get("/{tenant}/controller/v1/4712/deploymentBase/" + uaction.getId(), tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(action.getId()))))
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename",
contains("test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].name",
contains(ds.findFirstModuleByType(appType).get().getName())));
// Retrieved is reported
final Iterable<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusByAction(new PageRequest(0, 100, Direction.DESC, "id"), uaction.getId());
assertThat(actionStatusMessages).hasSize(2);
final ActionStatus actionStatusMessage = actionStatusMessages.iterator().next();
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
}
@Test
@Description("Checks that the deployementBase URL changes when the action is switched from soft to forced in TIMEFORCED case.")
public void changeEtagIfActionSwitchesFromSoftToForced() throws Exception {
// Prepare test data
final Target target = testdataFactory.createTarget("4712");
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final Long actionId = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED,
System.currentTimeMillis() + 2_000, Arrays.asList(target.getControllerId())).getActions().get(0);
MvcResult mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF)).andReturn();
final String urlBeforeSwitch = JsonPath.compile("_links.deploymentBase.href")
.read(mvcResult.getResponse().getContentAsString()).toString();
// Time is not yet over, so we should see the same URL
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF)).andReturn();
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
.toString()).isEqualTo(urlBeforeSwitch)
.startsWith("http://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/deploymentBase/" + actionId);
// After the time is over we should see a new etag
TimeUnit.MILLISECONDS.sleep(2_000);
mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF)).andReturn();
assertThat(JsonPath.compile("_links.deploymentBase.href").read(mvcResult.getResponse().getContentAsString())
.toString()).isNotEqualTo(urlBeforeSwitch);
}
@Test
@Description("Attempt/soft deployment to a controller. Checks if the resource reponse payload for a given deployment is as expected.")
public void deplomentAttemptAction() throws Exception {
// Prepare test data
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final String visibleMetadataOsKey = "metaDataVisible";
final String visibleMetadataOsValue = "withValue";
final int artifactSize = 5 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
false, artifactSize);
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
"test1.signature", false, artifactSize);
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true));
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
.key("metaDataNotVisible").value("withValue").targetVisible(false));
final Target savedTarget = testdataFactory.createTarget("4712");
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(0);
List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.SOFT,
RepositoryModelConstants.NO_FORCE_TIME, Arrays.asList(savedTarget.getControllerId()))
.getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(deploymentManagement.countActionsAll()).isEqualTo(1);
saved = assignDistributionSet(ds2, saved).getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionsAll()).isEqualTo(2);
final Action uaction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(uaction.getDistributionSet()).isEqualTo(ds);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
// Run test
final long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href", startsWith("http://localhost/"
+ tenantAware.getCurrentTenant() + "/controller/v1/4712/deploymentBase/" + uaction.getId())));
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(2);
final DistributionSet findDistributionSetByAction = distributionSetManagement.getByAction(action.getId()).get();
mvc.perform(
get("/{tenant}/controller/v1/4712/deploymentBase/" + uaction.getId(), tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(action.getId()))))
.andExpect(jsonPath("$.deployment.download", equalTo("attempt")))
.andExpect(jsonPath("$.deployment.update", equalTo("attempt")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].metadata[0].key").value(visibleMetadataOsKey))
.andExpect(
jsonPath("$.deployment.chunks[?(@.part==os)].metadata[0].value").value(visibleMetadataOsValue))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ getOsModule(findDistributionSetByAction) + "/artifacts/test1")))
.andExpect(
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ getOsModule(findDistributionSetByAction) + "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename",
contains("test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/"
+ getOsModule(findDistributionSetByAction) + "/artifacts/test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
+ "/controller/v1/4712/softwaremodules/" + getOsModule(findDistributionSetByAction)
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].metadata").doesNotExist())
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].name")
.value(ds.findFirstModuleByType(appType).get().getName()));
// Retrieved is reported
final List<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusByAction(new PageRequest(0, 100, Direction.DESC, "id"), uaction.getId()).getContent();
assertThat(actionStatusMessages).hasSize(2);
final ActionStatus actionStatusMessage = actionStatusMessages.iterator().next();
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
}
@Test
@Description("Attempt/soft deployment to a controller including automated switch to hard. Checks if the resource reponse payload for a given deployment is as expected.")
public void deplomentAutoForceAction() throws Exception {
// Prepare test data
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final int artifactSize = 5 * 1024;
final byte random[] = RandomUtils.nextBytes(artifactSize);
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
false, artifactSize);
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
"test1.signature", false, artifactSize);
final Target savedTarget = testdataFactory.createTarget("4712");
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).isEmpty();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(0);
List<Target> saved = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED,
System.currentTimeMillis(), Arrays.asList(savedTarget.getControllerId())).getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1);
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(deploymentManagement.countActionsAll()).isEqualTo(1);
saved = assignDistributionSet(ds2, saved).getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(2);
final Action uaction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
assertThat(uaction.getDistributionSet()).isEqualTo(ds);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(2);
// Run test
long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href", startsWith("http://localhost/"
+ tenantAware.getCurrentTenant() + "/controller/v1/4712/deploymentBase/" + uaction.getId())));
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4712").get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(2);
current = System.currentTimeMillis();
final DistributionSet findDistributionSetByAction = distributionSetManagement.getByAction(action.getId()).get();
mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/{actionId}", tenantAware.getCurrentTenant(),
uaction.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.id", equalTo(String.valueOf(action.getId()))))
.andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].name",
contains(ds.findFirstModuleByType(runtimeType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==jvm)].version",
contains(ds.findFirstModuleByType(runtimeType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].name",
contains(ds.findFirstModuleByType(osType).get().getName())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].version",
contains(ds.findFirstModuleByType(osType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].filename", contains("test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.md5",
contains(artifact.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0].hashes.sha1",
contains(artifact.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.download-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[0]._links.md5sum-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].size", contains(5 * 1024)))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].filename",
contains("test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.md5",
contains(artifactSignature.getMd5Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
contains(artifactSignature.getSha1Hash())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.signature")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
contains(
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
+ "/artifacts/test1.signature.MD5SUM")))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].version",
contains(ds.findFirstModuleByType(appType).get().getVersion())))
.andExpect(jsonPath("$.deployment.chunks[?(@.part==bApp)].name",
contains(ds.findFirstModuleByType(appType).get().getName())));
// Retrieved is reported
final Iterable<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusByAction(new PageRequest(0, 100, Direction.DESC, "id"), uaction.getId()).getContent();
assertThat(actionStatusMessages).hasSize(2);
final ActionStatus actionStatusMessage = actionStatusMessages.iterator().next();
assertThat(actionStatusMessage.getStatus()).isEqualTo(Status.RETRIEVED);
}
@Test
@Description("Test various invalid access attempts to the deployment resource und the expected behaviour of the server.")
public void badDeploymentAction() throws Exception {
final Target target = testdataFactory.createTarget("4712");
// not allowed methods
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/1", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/4712/deploymentBase/1", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/4712/deploymentBase/1", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
// non existing target
mvc.perform(get("/{tenant}/controller/v1/4715/deploymentBase/1", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// no deployment
mvc.perform(get("/controller/v1/4712/deploymentBase/1", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// wrong media type
final List<Target> toAssign = Arrays.asList(target);
final DistributionSet savedSet = testdataFactory.createDistributionSet("");
final Long actionId = assignDistributionSet(savedSet, toAssign).getActions().get(0);
mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/" + actionId, tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/" + actionId, tenantAware.getCurrentTenant())
.accept(MediaType.APPLICATION_ATOM_XML)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotAcceptable());
}
@Test
@Description("The server protects itself against to many feedback upload attempts. The test verfies that "
+ "it is not possible to exceed the configured maximum number of feedback uplods.")
public void tooMuchDeplomentActionFeedback() throws Exception {
final Target target = testdataFactory.createTarget("4712");
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "4712");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding");
// assign distribution set creates an action status, so only 99 left
for (int i = 0; i < 99; i++) {
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(feedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(feedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
}
@Test
@Description("The server protects itself against too large feedback bodies. The test verifies that "
+ "it is not possible to exceed the configured maximum number of feedback details.")
public void tooMuchDeploymentActionMessagesInFeedback() throws Exception {
final Target target = testdataFactory.createTarget("4712");
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "4712");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final List<String> messages = new ArrayList<>();
for (int i = 0; i < 51; i++) {
messages.add(String.valueOf(i));
}
final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding", "none",
messages);
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(feedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
}
@Test
@Description("Multiple uploads of deployment status feedback to the server.")
public void multipleDeplomentActionFeedback() throws Exception {
final Target savedTarget1 = testdataFactory.createTarget("4712");
testdataFactory.createTarget("4713");
testdataFactory.createTarget("4714");
final DistributionSet ds1 = testdataFactory.createDistributionSet("1", true);
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
final DistributionSet ds3 = testdataFactory.createDistributionSet("3", true);
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget1);
final Long actionId1 = assignDistributionSet(ds1.getId(), "4712").getActions().get(0);
final Long actionId2 = assignDistributionSet(ds2.getId(), "4712").getActions().get(0);
final Long actionId3 = assignDistributionSet(ds3.getId(), "4712").getActions().get(0);
Target myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(3);
assertThat(deploymentManagement.getAssignedDistributionSet(myT.getControllerId()).get()).isEqualTo(ds3);
assertThat(deploymentManagement.getInstalledDistributionSet(myT.getControllerId())).isNotPresent();
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.UNKNOWN)).hasSize(2);
// action1 done
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + actionId1 + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(actionId1.toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(2);
assertThat(deploymentManagement.getAssignedDistributionSet(myT.getControllerId()).get()).isEqualTo(ds3);
assertThat(deploymentManagement.getInstalledDistributionSet(myT.getControllerId()).get()).isEqualTo(ds1);
Iterable<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusAll(new PageRequest(0, 100, Direction.DESC, "id")).getContent();
assertThat(actionStatusMessages).hasSize(4);
assertThat(actionStatusMessages.iterator().next().getStatus()).isEqualTo(Status.FINISHED);
// action2 done
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + actionId2 + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(actionId2.toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(deploymentManagement.getAssignedDistributionSet(myT.getControllerId()).get()).isEqualTo(ds3);
assertThat(deploymentManagement.getInstalledDistributionSet(myT.getControllerId()).get()).isEqualTo(ds2);
actionStatusMessages = deploymentManagement.findActionStatusAll(new PageRequest(0, 100, Direction.DESC, "id"))
.getContent();
assertThat(actionStatusMessages).hasSize(5);
assertThat(actionStatusMessages).haveAtLeast(1, new ActionStatusCondition(Status.FINISHED));
// action3 done
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + actionId3 + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(actionId3.toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(0);
assertThat(deploymentManagement.getAssignedDistributionSet(myT.getControllerId()).get()).isEqualTo(ds3);
assertThat(deploymentManagement.getInstalledDistributionSet(myT.getControllerId()).get()).isEqualTo(ds3);
actionStatusMessages = deploymentManagement.findActionStatusAll(new PageRequest(0, 100, Direction.DESC, "id"))
.getContent();
assertThat(actionStatusMessages).hasSize(6);
assertThat(actionStatusMessages).haveAtLeast(1, new ActionStatusCondition(Status.FINISHED));
}
@Test
@Description("Verfies that an update action is correctly set to error if the controller provides error feedback.")
public void rootRsSingleDeplomentActionWithErrorFeedback() throws Exception {
DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget("4712");
final List<Target> toAssign = new ArrayList<>();
toAssign.add(savedTarget);
assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.UNKNOWN);
assignDistributionSet(ds, toAssign);
final Action action = deploymentManagement.findActionsByDistributionSet(PAGE, ds.getId()).getContent().get(0);
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed", "failure",
"error message"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
Target myT = targetManagement.getByControllerID("4712").get();
assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.ERROR);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(0);
assertThat(deploymentManagement.countActionsByTarget(myT.getControllerId())).isEqualTo(1);
final Iterable<ActionStatus> actionStatusMessages = deploymentManagement
.findActionStatusAll(new PageRequest(0, 100, Direction.DESC, "id")).getContent();
assertThat(actionStatusMessages).hasSize(2);
assertThat(actionStatusMessages).haveAtLeast(1, new ActionStatusCondition(Status.ERROR));
// redo
ds = distributionSetManagement.getWithDetails(ds.getId()).get();
assignDistributionSet(ds, Arrays.asList(targetManagement.getByControllerID("4712").get()));
final Action action2 = deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId()).getContent()
.get(0);
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action2.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action2.getId().toString(), "closed", "success"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(1);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(0);
assertThat(deploymentManagement.findInActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(2);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(4);
assertThat(deploymentManagement.findActionStatusByAction(PAGE, action.getId()).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.ERROR));
assertThat(deploymentManagement.findActionStatusByAction(PAGE, action2.getId()).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.FINISHED));
}
@Test
@Description("Verfies that the controller can provided as much feedback entries as necessry as long as it is in the configured limites.")
public void rootRsSingleDeplomentActionFeedback() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
final Target savedTarget = testdataFactory.createTarget("4712");
final List<Target> toAssign = Arrays.asList(savedTarget);
Target myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.UNKNOWN);
assignDistributionSet(ds, toAssign);
final Action action = deploymentManagement.findActionsByDistributionSet(PAGE, ds.getId()).getContent().get(0);
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(targetManagement.findByInstalledDistributionSet(PAGE, ds.getId())).hasSize(0);
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds.getId())).hasSize(1);
// Now valid Feedback
for (int i = 0; i < 4; i++) {
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "proceeding"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(5);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(5,
new ActionStatusCondition(Status.RUNNING));
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "scheduled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(6);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(5,
new ActionStatusCondition(Status.RUNNING));
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "resumed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(0);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(7);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(6,
new ActionStatusCondition(Status.RUNNING));
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "canceled"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.PENDING)).hasSize(1);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(0);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(8);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(7,
new ActionStatusCondition(Status.RUNNING));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.CANCELED));
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "rejected"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(9);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(6,
new ActionStatusCondition(Status.RUNNING));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.WARNING));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.CANCELED));
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
myT = targetManagement.getByControllerID("4712").get();
assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, myT.getControllerId())).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.ERROR)).hasSize(0);
assertThat(targetManagement.findByUpdateStatus(new PageRequest(0, 10), TargetUpdateStatus.IN_SYNC)).hasSize(1);
assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(10);
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(7,
new ActionStatusCondition(Status.RUNNING));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.WARNING));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.CANCELED));
assertThat(deploymentManagement.findActionStatusAll(PAGE).getContent()).haveAtLeast(1,
new ActionStatusCondition(Status.FINISHED));
assertThat(targetManagement.findByInstalledDistributionSet(PAGE, ds.getId())).hasSize(1);
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds.getId())).hasSize(1);
}
@Test
@Description("Various forbidden request appempts on the feedback resource. Ensures correct answering behaviour as expected to these kind of errors.")
public void badDeplomentActionFeedback() throws Exception {
final DistributionSet savedSet = testdataFactory.createDistributionSet("");
final DistributionSet savedSet2 = testdataFactory.createDistributionSet("1");
// target does not exist
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/1234/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionInProgressFeedback("1234")).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
Target savedTarget = testdataFactory.createTarget("4712");
final Target savedTarget2 = testdataFactory.createTarget("4713");
// Action does not exists
mvc.perform(post("/{tenant}/controller/v1/4712/deploymentBase/1234/feedback", tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionInProgressFeedback("1234")).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
final List<Target> toAssign = Arrays.asList(savedTarget);
final List<Target> toAssign2 = Arrays.asList(savedTarget2);
savedTarget = assignDistributionSet(savedSet, toAssign).getAssignedEntity().iterator().next();
assignDistributionSet(savedSet2, toAssign2);
final Action updateAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
// action exists but is not assigned to this target
mvc.perform(post("/{tenant}/controller/v1/4713/deploymentBase/" + updateAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionInProgressFeedback(updateAction.getId().toString()))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
// not allowed methods
mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/2/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(put("/{tenant}/controller/v1/4712/deploymentBase/2/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/{tenant}/controller/v1/4712/deploymentBase/2/feedback", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isMethodNotAllowed());
}
@Test
@Description("Ensures that an invalid id in feedback body returns a bad request.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void invalidIdInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
mvc.perform(post("/{tenant}/controller/v1/1080/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(JsonBuilder.deploymentActionInProgressFeedback("AAAA"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@Test
@Description("Ensures that a missing feedback result in feedback body returns a bad request.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void missingResultAttributeInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String missingResultInFeedback = JsonBuilder.missingResultInFeedback(action.getId().toString(), "closed",
"test");
mvc.perform(post("/{tenant}/controller/v1/1080/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(missingResultInFeedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@Test
@Description("Ensures that a missing finished result in feedback body returns a bad request.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) })
public void missingFinishedAttributeInFeedbackReturnsBadRequest() throws Exception {
final Target target = testdataFactory.createTarget("1080");
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "1080");
final Action action = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()
.get(0);
final String missingFinishedResultInFeedback = JsonBuilder
.missingFinishedResultInFeedback(action.getId().toString(), "closed", "test");
mvc.perform(post("/{tenant}/controller/v1/1080/deploymentBase/" + action.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(missingFinishedResultInFeedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
private class ActionStatusCondition extends Condition<ActionStatus> {
private final Status status;
public ActionStatusCondition(final Status status) {
this.status = status;
}
@Override
public boolean matches(final ActionStatus actionStatus) {
return actionStatus.getStatus() == status;
}
}
}

View File

@@ -0,0 +1,590 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.CONTROLLER_ROLE_ANONYMOUS;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION;
import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions.SYSTEM_ROLE;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.startsWith;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleTypeCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.eclipse.hawkbit.util.IpUtil;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test the root controller resources.
*/
@Features("Component Tests - Direct Device Integration API")
@Stories("Root Poll Resource")
public class DdiRootControllerTest extends AbstractDDiApiIntegrationTest {
private static final String TARGET_COMPLETED_INSTALLATION_MSG = "Target completed installation.";
private static final String TARGET_PROCEEDING_INSTALLATION_MSG = "Target proceeding installation.";
private static final String TARGET_SCHEDULED_INSTALLATION_MSG = "Target scheduled installation.";
@Autowired
private HawkbitSecurityProperties securityProperties;
@Test
@Description("Ensures that targets cannot be created e.g. in plug'n play scenarios when tenant does not exists but can be created if the tenant exists.")
@WithUser(tenantId = "tenantDoesNotExists", allSpPermissions = true, authorities = { CONTROLLER_ROLE,
SYSTEM_ROLE }, autoCreateTenant = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = DistributionSetTypeCreatedEvent.class, count = 3),
@Expect(type = SoftwareModuleTypeCreatedEvent.class, count = 2) })
public void targetCannotBeRegisteredIfTenantDoesNotExistsButWhenExists() throws Exception {
mvc.perform(get("/default-tenant/", tenantAware.getCurrentTenant())).andDo(MockMvcResultPrinter.print())
.andExpect(status().isNotFound());
// create tenant -- creates softwaremoduletypes and distributionsettypes
systemManagement.getTenantMetadata("tenantDoesNotExists");
mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// delete tenant again, will also deleted target aControllerId
systemManagement.deleteTenant("tenantDoesNotExists");
mvc.perform(get("/{}/controller/v1/aControllerId", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isBadRequest());
}
@Test
@Description("Ensures that target poll request does not change audit data on the entity.")
@WithUser(principal = "knownPrincipal", authorities = { SpPermission.READ_TARGET, SpPermission.UPDATE_TARGET,
SpPermission.CREATE_TARGET }, allSpPermissions = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void targetPollDoesNotModifyAuditData() throws Exception {
// create target first with "knownPrincipal" user and audit data
final String knownTargetControllerId = "target1";
final String knownCreatedBy = "knownPrincipal";
testdataFactory.createTarget(knownTargetControllerId);
final Target findTargetByControllerID = targetManagement.getByControllerID(knownTargetControllerId).get();
assertThat(findTargetByControllerID.getCreatedBy()).isEqualTo(knownCreatedBy);
assertThat(findTargetByControllerID.getCreatedAt()).isNotNull();
// make a poll, audit information should not be changed, run as
// controller principal!
securityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get("/{tenant}/controller/v1/" + knownTargetControllerId, tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
// verify that audit information has not changed
final Target targetVerify = targetManagement.getByControllerID(knownTargetControllerId).get();
assertThat(targetVerify.getCreatedBy()).isEqualTo(findTargetByControllerID.getCreatedBy());
assertThat(targetVerify.getCreatedAt()).isEqualTo(findTargetByControllerID.getCreatedAt());
assertThat(targetVerify.getLastModifiedBy()).isEqualTo(findTargetByControllerID.getLastModifiedBy());
assertThat(targetVerify.getLastModifiedAt()).isEqualTo(findTargetByControllerID.getLastModifiedAt());
}
@Test
@Description("Ensures that server returns a not found response in case of empty controlloer ID.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void rootRsWithoutId() throws Exception {
mvc.perform(get("/controller/v1/")).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
}
@Test
@Description("Ensures that the system creates a new target in plug and play manner, i.e. target is authenticated but does not exist yet.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlay() throws Exception {
final long current = System.currentTimeMillis();
mvc.perform(get("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")));
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.REGISTERED);
// not allowed methods
mvc.perform(post("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(put("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
mvc.perform(delete("/default-tenant/controller/v1/4711")).andDo(MockMvcResultPrinter.print())
.andExpect(status().isMethodNotAllowed());
}
@Test
@Description("Ensures that tenant specific polling time, which is saved in the db, is delivered to the controller.")
@WithUser(principal = "knownpricipal", allSpPermissions = false)
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void pollWithModifiedGloablPollingTime() throws Exception {
securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:02:00");
return null;
});
securityRule.runAs(WithSpringAuthorityRule.withUser("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:02:00")));
return null;
});
}
@Test
@Description("Ensures that etag check results in not modified response if provided etag by client is identical to entity in repository.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 6),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
public void rootRsNotModified() throws Exception {
final String etag = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00"))).andReturn().getResponse()
.getHeader("ETag");
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match", etag))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
final Target target = targetManagement.getByControllerID("4711").get();
final DistributionSet ds = testdataFactory.createDistributionSet("");
assignDistributionSet(ds.getId(), "4711");
final Action updateAction = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
final String etagWithFirstUpdate = mvc
.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header("If-None-Match", etag).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4711/deploymentBase/" + updateAction.getId())))
.andReturn().getResponse().getHeader("ETag");
assertThat(etagWithFirstUpdate).isNotNull();
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match",
etagWithFirstUpdate)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
// now lets finish the update
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + updateAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(updateAction.getId().toString(), "closed"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// we are again at the original state
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match", etag))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified());
// Now another deployment
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
assignDistributionSet(ds2.getId(), "4711");
final Action updateAction2 = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header("If-None-Match", etagWithFirstUpdate).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")))
.andExpect(jsonPath("$._links.deploymentBase.href",
startsWith("http://localhost/" + tenantAware.getCurrentTenant()
+ "/controller/v1/4711/deploymentBase/" + updateAction2.getId())))
.andReturn().getResponse().getHeader("ETag");
}
@Test
@Description("Ensures that the target state machine of a precomissioned target switches from "
+ "UNKNOWN to REGISTERED when the target polls for the first time.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPrecommissioned() throws Exception {
final Target target = testdataFactory.createTarget("4711");
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.UNKNOWN);
final long current = System.currentTimeMillis();
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(content().contentType(APPLICATION_JSON_HAL_UTF))
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:01:00")));
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
.isLessThanOrEqualTo(System.currentTimeMillis());
assertThat(targetManagement.getByControllerID("4711").get().getLastTargetQuery())
.isGreaterThanOrEqualTo(current);
assertThat(targetManagement.getByControllerID("4711").get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.REGISTERED);
}
@Test
@Description("Ensures that the source IP address of the polling target is correctly stored in repository")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsPlugAndPlayIpAddress() throws Exception {
// test
final String knownControllerId1 = "0815";
final long create = System.currentTimeMillis();
// make a poll, audit information should be set on plug and play
securityRule.runAs(WithSpringAuthorityRule.withController("controller", CONTROLLER_ROLE_ANONYMOUS), () -> {
mvc.perform(
get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
return null;
});
// verify
final Target target = targetManagement.getByControllerID(knownControllerId1).get();
assertThat(target.getAddress()).isEqualTo(IpUtil.createHttpUri("127.0.0.1"));
assertThat(target.getCreatedBy()).isEqualTo("CONTROLLER_PLUG_AND_PLAY");
assertThat(target.getCreatedAt()).isGreaterThanOrEqualTo(create);
assertThat(target.getLastModifiedBy()).isEqualTo("CONTROLLER_PLUG_AND_PLAY");
assertThat(target.getLastModifiedAt()).isGreaterThanOrEqualTo(create);
}
@Test
@Description("Ensures that the source IP address of the polling target is not stored in repository if disabled")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void rootRsIpAddressNotStoredIfDisabled() throws Exception {
securityProperties.getClients().setTrackRemoteIp(false);
// test
final String knownControllerId1 = "0815";
mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), knownControllerId1))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
// verify
final Target target = targetManagement.getByControllerID(knownControllerId1).get();
assertThat(target.getAddress()).isEqualTo(IpUtil.createHttpUri("***"));
securityProperties.getClients().setTrackRemoteIp(true);
}
@Test
@Description("Controller trys to finish an update process after it has been finished by an error action status.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next();
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(
JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "failure"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant()).content(
JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "success"))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isGone());
}
@Test
@Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryCount() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next();
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled",
TARGET_SCHEDULED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding",
TARGET_PROCEEDING_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed",
"success", TARGET_COMPLETED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=3",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_PROCEEDING_INSTALLATION_MSG))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG))));
}
@Test
@Description("Test to verify that a zero input value of actionHistory results in no action history appended for getControllerDeploymentActionFeedback endpoint.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryZeroInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next();
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled",
TARGET_SCHEDULED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding",
TARGET_PROCEEDING_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed",
"success", TARGET_COMPLETED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-2",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
}
@Test
@Description("Test to verify that entire action history is returned if the input value for actionHistory is -1, for getControllerDeploymentActionFeedback endpoint.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
public void testActionHistoryNegativeInput() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
Target savedTarget = testdataFactory.createTarget("911");
savedTarget = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getAssignedEntity().iterator()
.next();
final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled",
TARGET_SCHEDULED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding",
TARGET_PROCEEDING_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(post("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "/feedback",
tenantAware.getCurrentTenant())
.content(JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed",
"success", TARGET_COMPLETED_INSTALLATION_MSG))
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-1",
tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_SCHEDULED_INSTALLATION_MSG))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_PROCEEDING_INSTALLATION_MSG))))
.andExpect(jsonPath("$.actionHistory.messages",
hasItem(containsString(TARGET_COMPLETED_INSTALLATION_MSG))));
}
@Test
@Description("Test the polling time based on different maintenance window start and end time.")
public void sleepTimeResponseForDifferentMaintenanceWindowParameters() throws Exception {
final DistributionSet ds = testdataFactory.createDistributionSet("");
securityRule.runAs(WithSpringAuthorityRule.withUser("tenantadmin", HAS_AUTH_TENANT_CONFIGURATION), () -> {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.POLLING_TIME_INTERVAL,
"00:05:00");
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MIN_POLLING_TIME_INTERVAL,
"00:01:00");
return null;
});
final Target savedTarget = testdataFactory.createTarget("1911");
assignDistributionSetWithMaintenanceWindow(ds.getId(), savedTarget.getControllerId(), getTestSchedule(16),
getTestDuration(10), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", greaterThanOrEqualTo("00:05:00")));
final Target savedTarget1 = testdataFactory.createTarget("2911");
final DistributionSet ds1 = testdataFactory.createDistributionSet("1");
assignDistributionSetWithMaintenanceWindow(ds1.getId(), savedTarget1.getControllerId(), getTestSchedule(10),
getTestDuration(10), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/2911/")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", lessThan("00:05:00")))
.andExpect(jsonPath("$.config.polling.sleep", greaterThanOrEqualTo("00:03:00")));
final Target savedTarget2 = testdataFactory.createTarget("3911");
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
assignDistributionSetWithMaintenanceWindow(ds2.getId(), savedTarget2.getControllerId(), getTestSchedule(5),
getTestDuration(5), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/3911/")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", lessThan("00:02:00")));
final Target savedTarget3 = testdataFactory.createTarget("4911");
final DistributionSet ds3 = testdataFactory.createDistributionSet("3");
assignDistributionSetWithMaintenanceWindow(ds3.getId(), savedTarget3.getControllerId(), getTestSchedule(-5),
getTestDuration(15), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/4911/")).andExpect(status().isOk())
.andExpect(jsonPath("$.config.polling.sleep", equalTo("00:05:00")));
}
@Test
@Description("Test download and update values before maintenance window start time.")
public void downloadAndUpdateStatusBeforeMaintenanceWindowStartTime() throws Exception {
Target savedTarget = testdataFactory.createTarget("1911");
final DistributionSet ds = testdataFactory.createDistributionSet("");
savedTarget = assignDistributionSetWithMaintenanceWindow(ds.getId(), savedTarget.getControllerId(),
getTestSchedule(2), getTestDuration(1), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk());
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/1911/deploymentBase/{actionId}", tenantAware.getCurrentTenant(),
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("skip")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("unavailable")));
}
@Test
@Description("Test download and update values after maintenance window start time.")
public void downloadAndUpdateStatusDuringMaintenanceWindow() throws Exception {
Target savedTarget = testdataFactory.createTarget("1911");
final DistributionSet ds = testdataFactory.createDistributionSet("");
savedTarget = assignDistributionSetWithMaintenanceWindow(ds.getId(), savedTarget.getControllerId(),
getTestSchedule(-5), getTestDuration(10), getTestTimeZone()).getAssignedEntity().iterator().next();
mvc.perform(get("/default-tenant/controller/v1/1911/")).andExpect(status().isOk());
final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())
.getContent().get(0);
mvc.perform(get("/{tenant}/controller/v1/1911/deploymentBase/{actionId}", tenantAware.getCurrentTenant(),
action.getId()).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
.andExpect(status().isOk()).andExpect(jsonPath("$.deployment.download", equalTo("forced")))
.andExpect(jsonPath("$.deployment.update", equalTo("forced")))
.andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("available")));
}
}

View File

@@ -0,0 +1,177 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.ddi.rest.resource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.Arrays;
import java.util.List;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.rest.filter.ExcludePathAwareShallowETagFilter;
import org.eclipse.hawkbit.rest.util.JsonBuilder;
import org.eclipse.hawkbit.security.DosFilter;
import org.junit.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import com.google.common.net.HttpHeaders;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test potential DOS attack scenarios and check if the filter prevents them.
*
*/
@ActiveProfiles({ "test" })
@Features("Component Tests - REST Security")
@Stories("Denial of Service protection filter")
public class DosFilterTest extends AbstractDDiApiIntegrationTest {
@Override
protected DefaultMockMvcBuilder createMvcWebAppContext(final WebApplicationContext context) {
return MockMvcBuilders.webAppContextSetup(context)
.addFilter(new DosFilter(null, 10, 10, "127\\.0\\.0\\.1|\\[0:0:0:0:0:0:0:1\\]", "(^192\\.168\\.)",
"X-Forwarded-For"))
.addFilter(new ExcludePathAwareShallowETagFilter(
"/rest/v1/softwaremodules/{smId}/artifacts/{artId}/download",
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/**",
"/api/v1/downloadserver/**"));
}
@Test
@Description("Ensures that clients that are on the blacklist are forbidded ")
public void blackListedClientIsForbidden() throws Exception {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "192.168.0.4 , 10.0.0.1 ")).andExpect(status().isForbidden());
}
@Test
@Description("Ensures that a READ DoS attempt is blocked ")
public void getFloddingAttackThatisPrevented() throws Exception {
MvcResult result = null;
int requests = 0;
do {
result = mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "10.0.0.1")).andReturn();
requests++;
// we give up after 1.000 requests
assertThat(requests).isLessThan(1_000);
} while (result.getResponse().getStatus() != HttpStatus.TOO_MANY_REQUESTS.value());
// the filter shuts down after 100 GET requests
assertThat(requests).isGreaterThanOrEqualTo(10);
}
@Test
@Description("Ensures that an assumed READ DoS attempt is not blocked as the client (with IPv4 address) is on a whitelist")
public void unacceptableGetLoadButOnWhitelistIPv4() throws Exception {
for (int i = 0; i < 100; i++) {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "127.0.0.1")).andExpect(status().isOk());
}
}
@Test
@Description("Ensures that an assumed READ DoS attempt is not blocked as the client (with IPv6 address) is on a whitelist")
public void unacceptableGetLoadButOnWhitelistIPv6() throws Exception {
for (int i = 0; i < 100; i++) {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "0:0:0:0:0:0:0:1")).andExpect(status().isOk());
}
}
@Test
@Description("Ensures that a relatively high number of READ requests is allowed if it is below the DoS detection threshold")
public void acceptableGetLoad() throws Exception {
for (int x = 0; x < 3; x++) {
// sleep for one second
Thread.sleep(1100);
for (int i = 0; i < 9; i++) {
mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant())
.header(HttpHeaders.X_FORWARDED_FOR, "10.0.0.1")).andExpect(status().isOk());
}
}
}
@Test
@Description("Ensures that a WRITE DoS attempt is blocked ")
public void putPostFloddingAttackThatisPrevented() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");
MvcResult result = null;
int requests = 0;
do {
result = mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
tenantAware.getCurrentTenant()).header(HttpHeaders.X_FORWARDED_FOR, "10.0.0.1").content(feedback)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
.andReturn();
requests++;
// we give up after 500 requests
assertThat(requests).isLessThan(500);
} while (result.getResponse().getStatus() != HttpStatus.TOO_MANY_REQUESTS.value());
// the filter shuts down after 10 POST requests
assertThat(requests).isGreaterThanOrEqualTo(10);
}
@Test
@Description("Ensures that a relatively high number of WRITE requests is allowed if it is below the DoS detection threshold")
public void acceptablePutPostLoad() throws Exception {
final Long actionId = prepareDeploymentBase();
final String feedback = JsonBuilder.deploymentActionFeedback(actionId.toString(), "proceeding");
for (int x = 0; x < 5; x++) {
// sleep for one second
Thread.sleep(1100);
for (int i = 0; i < 9; i++) {
mvc.perform(post("/{tenant}/controller/v1/4711/deploymentBase/" + actionId + "/feedback",
tenantAware.getCurrentTenant()).header(HttpHeaders.X_FORWARDED_FOR, "10.0.0.1")
.content(feedback).contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
}
}
private Long prepareDeploymentBase() {
final DistributionSet ds = testdataFactory.createDistributionSet("test");
final Target target = testdataFactory.createTarget("4711");
final List<Target> toAssign = Arrays.asList(target);
final Iterable<Target> saved = assignDistributionSet(ds, toAssign).getAssignedEntity();
assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())).hasSize(1);
final Action uaction = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId())
.getContent().get(0);
return uaction.getId();
}
}

View File

@@ -0,0 +1,25 @@
#
# Copyright (c) 2015 Bosch Software Innovations GmbH and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
# http://www.eclipse.org/legal/epl-v10.html
#
# DDI configuration - START
hawkbit.controller.pollingTime=00:01:00
hawkbit.controller.pollingOverdueTime=00:01:00
hawkbit.controller.minPollingTime=00:00:30
hawkbit.controller.maintenanceWindowPollCount=3
# DDI configuration - END
# Upload configuration - START
spring.http.multipart.max-file-size=5MB
# Upload configuration - END
# Quota - START
hawkbit.server.security.dos.maxStatusEntriesPerAction=100
hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10
# Quota - END

View File

@@ -0,0 +1,17 @@
# Eclipse.IoT hawkBit - Management API - Model and Resources
This Management (Mgmt) API is used to manage and monitor the HawkBit Update Server via HTTP. This API allows Create/Read/Update/Delete operations for provisioning targets (i.e. devices) and repository content (i.e. software).
# Version 1
The model follows [semantic versioning](http://semver.org) with MAJOR version, i.e. breaking changes will result in a new MAJOR version.
# Compile
#### Build hawkbit-mgmt-api
```
$ cd hawkbit/hawkbit-mgmt-api
$ mvn clean install
```

View File

@@ -0,0 +1,48 @@
<!--
Copyright (c) 2015 Bosch Software Innovations GmbH and others.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.eclipse.hawkbit</groupId>
<artifactId>hawkbit-rest-parent</artifactId>
<version>0.2.0-SNAPSHOT</version>
</parent>
<artifactId>hawkbit-mgmt-api</artifactId>
<name>hawkBit :: REST :: Management API</name>
<dependencies>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ru.yandex.qatools.allure</groupId>
<artifactId>allure-junit-adaptor</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for BaseEntity to RESTful API representation.
*
*/
public abstract class MgmtBaseEntity extends ResourceSupport {
@JsonProperty
private String createdBy;
@JsonProperty
private Long createdAt;
@JsonProperty
private String lastModifiedBy;
@JsonProperty
private Long lastModifiedAt;
/**
* @return the createdBy
*/
public String getCreatedBy() {
return createdBy;
}
/**
* @param createdBy
* the createdBy to set
*/
@JsonIgnore
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}
/**
* @return the createdAt
*/
public Long getCreatedAt() {
return createdAt;
}
/**
* @param createdAt
* the createdAt to set
*/
@JsonIgnore
public void setCreatedAt(final Long createdAt) {
this.createdAt = createdAt;
}
/**
* @return the lastModifiedBy
*/
public String getLastModifiedBy() {
return lastModifiedBy;
}
/**
* @param lastModifiedBy
* the lastModifiedBy to set
*/
@JsonIgnore
public void setLastModifiedBy(final String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
/**
* @return the lastModifiedAt
*/
public Long getLastModifiedAt() {
return lastModifiedAt;
}
/**
* @param lastModifiedAt
* the lastModifiedAt to set
*/
@JsonIgnore
public void setLastModifiedAt(final Long lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A generic abstract rest model which contains only a ID for use-case e.g.
* which allows only posting or putting an ID into the request body, e.g. for
* assignments.
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtId {
@JsonProperty
private Long id;
/**
* @return the id
*/
public Long getId() {
return id;
}
/**
* @param id
* the id to set
*/
public void setId(final Long id) {
this.id = id;
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) Siemens AG, 2018
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* JSON model for Management API to define the maintenance window.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMaintenanceWindow extends MgmtMaintenanceWindowRequestBody {
/**
* Time in {@link TimeUnit#MILLISECONDS} of the next maintenance window
* start
*/
@JsonProperty
private long nextStartAt;
public long getNextStartAt() {
return nextStartAt;
}
public void setNextStartAt(final long nextStartAt) {
this.nextStartAt = nextStartAt;
}
}

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2018 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request body for maintenance window PUT/POST commands, based on a schedule
* defined as cron expression, duration in HH:mm:ss format and time zone as
* offset from UTC.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMaintenanceWindowRequestBody {
@JsonProperty
private String schedule;
@JsonProperty
private String duration;
@JsonProperty
private String timezone;
public String getSchedule() {
return schedule;
}
/**
* @param schedule
* is the cron expression to be used for scheduling maintenance
* window(s). Expression has 6 mandatory fields and a last
* optional field: "second minute hour dayofmonth month weekday
* year".
*/
public void setSchedule(final String schedule) {
this.schedule = schedule;
}
public String getDuration() {
return duration;
}
/**
* @param duration
* in HH:mm:ss format specifying the duration of a maintenance
* window, for example 00:30:00 for 30 minutes.
*/
public void setDuration(final String duration) {
this.duration = duration;
}
public String getTimezone() {
return timezone;
}
/**
* @param timezone
* is the time zone specified as +/-hh:mm offset from UTC. For
* example +02:00 for CET summer time and +00:00 for UTC. The
* start time of a maintenance window calculated based on the
* cron expression is relative to this time zone.
*/
public void setTimezone(final String timezone) {
this.timezone = timezone;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The representation of an meta data in the REST API for POST/Create.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMetadata {
@JsonProperty(required = true)
private String key;
@JsonProperty
private String value;
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The representation of an meta data in the REST API for PUT/Update.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtMetadataBodyPut {
@JsonProperty
private String value;
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for NamedEntity to RESTful API representation.
*
*/
public abstract class MgmtNamedEntity extends MgmtBaseEntity {
@JsonProperty(required = true)
private String name;
@JsonProperty
private String description;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description
* the description to set
*/
public void setDescription(final String description) {
this.description = description;
}
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for PollStatus to RESTful API representation.
*
*
*
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtPollStatus {
@JsonProperty
private Long lastRequestAt;
@JsonProperty
private Long nextExpectedRequestAt;
@JsonProperty
private boolean overdue;
/**
* @return the lastRequestAt
*/
public Long getLastRequestAt() {
return lastRequestAt;
}
/**
* @param lastRequestAt
* the lastRequestAt to set
*/
public void setLastRequestAt(final Long lastRequestAt) {
this.lastRequestAt = lastRequestAt;
}
/**
* @return the nextExpectedRequestAt
*/
public Long getNextExpectedRequestAt() {
return nextExpectedRequestAt;
}
/**
* @param nextExpectedRequestAt
* the nextExpectedRequestAt to set
*/
public void setNextExpectedRequestAt(final Long nextExpectedRequestAt) {
this.nextExpectedRequestAt = nextExpectedRequestAt;
}
/**
* @return the overdue
*/
public boolean isOverdue() {
return overdue;
}
/**
* @param overdue
* the overdue to set
*/
public void setOverdue(final boolean overdue) {
this.overdue = overdue;
}
}

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model;
import java.util.Collections;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A list representation with meta data for pagination, e.g. containing the
* total elements and size of content. The content of the actual list is stored
* in the {@link #content} field.
*
* @param <T>
* the type of elements in this list
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
public class PagedList<T> extends ResourceSupport {
@JsonProperty
private final List<T> content;
@JsonProperty
private final long total;
private final int size;
/**
* creates a new paged list with the given {@code content} and {@code total}
* .
*
* @param content
* the actual content of the list
* @param total
* the total amount of elements
* @throws NullPointerException
* in case {@code content} is {@code null}.
*/
@JsonCreator
public PagedList(@JsonProperty("content") @NotNull final List<T> content, @JsonProperty("total") final long total) {
this.size = content.size();
this.total = total;
this.content = content;
}
/**
* @return the size of the content list
*/
public int getSize() {
return size;
}
/**
* @return the total amount of elements
*/
public long getTotal() {
return total;
}
public List<T> getContent() {
return Collections.unmodifiableList(content);
}
}

View File

@@ -0,0 +1,114 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.action;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindow;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Action to RESTful API representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtAction extends MgmtBaseEntity {
/**
* API definition for action in update mode.
*/
public static final String ACTION_UPDATE = "update";
/**
* API definition for action in canceling.
*/
public static final String ACTION_CANCEL = "cancel";
/**
* API definition for action completed.
*/
public static final String ACTION_FINISHED = "finished";
/**
* API definition for action still active.
*/
public static final String ACTION_PENDING = "pending";
@JsonProperty("id")
private Long actionId;
@JsonProperty
private String type;
@JsonProperty
private String status;
@JsonProperty
private Long forceTime;
@JsonProperty
private MgmtActionType forceType;
@JsonProperty
private MgmtMaintenanceWindow maintenanceWindow;
public MgmtMaintenanceWindow getMaintenanceWindow() {
return maintenanceWindow;
}
public void setMaintenanceWindow(final MgmtMaintenanceWindow maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
public Long getForceTime() {
return forceTime;
}
public void setForceTime(final Long forceTime) {
this.forceTime = forceTime;
}
public MgmtActionType getForceType() {
return forceType;
}
public void setForceType(final MgmtActionType forceType) {
this.forceType = forceType;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
public Long getActionId() {
return actionId;
}
public void setActionId(final Long actionId) {
this.actionId = actionId;
}
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.action;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated model for Action updates in RESTful API representation.
*
*/
public class MgmtActionRequestBodyPut {
@JsonProperty
private MgmtActionType forceType;
public MgmtActionType getForceType() {
return forceType;
}
public void setForceType(final MgmtActionType forceType) {
this.forceType = forceType;
}
}

View File

@@ -0,0 +1,98 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.action;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for ActionStatus to RESTful API representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtActionStatus {
@JsonProperty("id")
private Long statusId;
@JsonProperty
private String type;
@JsonProperty
private List<String> messages;
@JsonProperty
private Long reportedAt;
/**
* @return the statusId
*/
public Long getStatusId() {
return statusId;
}
/**
* @param statusId
* the statusId to set
*/
public void setStatusId(final Long statusId) {
this.statusId = statusId;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type
* the type to set
*/
public void setType(final String type) {
this.type = type;
}
/**
* @return the messages
*/
public List<String> getMessages() {
return messages;
}
/**
* @param messages
* the messages to set
*/
public void setMessages(final List<String> messages) {
this.messages = messages;
}
/**
* @return the reportedAt
*/
public Long getReportedAt() {
return reportedAt;
}
/**
* @param reportedAt
* the reportedAt to set
*/
public void setReportedAt(final Long reportedAt) {
this.reportedAt = reportedAt;
}
}

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.artifact;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Artifact to RESTful API representation.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtArtifact extends MgmtBaseEntity {
@JsonProperty("id")
private Long artifactId;
@JsonProperty
private MgmtArtifactHash hashes;
@JsonProperty
private String providedFilename;
@JsonProperty
private Long size;
public MgmtArtifact() {
// need for json encoder
}
/**
* @param hashes
* the hashes to set
*/
@JsonIgnore
public void setHashes(final MgmtArtifactHash hashes) {
this.hashes = hashes;
}
/**
* @param artifactId
* the artifactId to set
*/
@JsonIgnore
public void setArtifactId(final Long artifactId) {
this.artifactId = artifactId;
}
/**
* @return the artifactId
*/
public Long getArtifactId() {
return artifactId;
}
/**
* @return the hashes
*/
public MgmtArtifactHash getHashes() {
return hashes;
}
/**
* @return the providedFilename
*/
public String getProvidedFilename() {
return providedFilename;
}
/**
* @param providedFilename
* the providedFilename to set
*/
@JsonIgnore
public void setProvidedFilename(final String providedFilename) {
this.providedFilename = providedFilename;
}
/**
* @return the size
*/
public Long getSize() {
return size;
}
/**
* @param size
* the size to set
*/
@JsonIgnore
public void setSize(final Long size) {
this.size = size;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.artifact;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Hashes for given Artifact.
*
*
*/
public class MgmtArtifactHash {
@JsonProperty
private String sha1;
@JsonProperty
private String md5;
/**
* Default constructor.
*/
public MgmtArtifactHash() {
// used for jackson to instantiate
}
/**
* Public constructor.
*/
public MgmtArtifactHash(final String sha1, final String md5) {
this.sha1 = sha1;
this.md5 = md5;
}
public String getSha1() {
return sha1;
}
public String getMd5() {
return md5;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* Definition of the Action type for the REST management API.
*
*/
public enum MgmtActionType {
/**
* The soft action type.
*/
SOFT("soft"),
/**
* The forced action type.
*/
FORCED("forced"),
/**
* The time forced action type.
*/
TIMEFORCED("timeforced");
private final String name;
private MgmtActionType(final String name) {
this.name = name;
}
@JsonValue
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for DistributionSet to RESTful API
* representation.
*
*
*
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSet extends MgmtNamedEntity {
@JsonProperty(value = "id", required = true)
private Long dsId;
@JsonProperty
private String version;
@JsonProperty
private List<MgmtSoftwareModule> modules = new ArrayList<>();
@JsonProperty
private boolean requiredMigrationStep;
@JsonProperty
private String type;
@JsonProperty
private Boolean complete;
@JsonProperty
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public Long getDsId() {
return dsId;
}
@JsonIgnore
public void setDsId(final Long id) {
dsId = id;
}
public String getVersion() {
return version;
}
public void setVersion(final String version) {
this.version = version;
}
public boolean isRequiredMigrationStep() {
return requiredMigrationStep;
}
public void setRequiredMigrationStep(final boolean requiredMigrationStep) {
this.requiredMigrationStep = requiredMigrationStep;
}
public List<MgmtSoftwareModule> getModules() {
return modules;
}
public void setModules(final List<MgmtSoftwareModule> modules) {
this.modules = modules;
}
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
public Boolean getComplete() {
return complete;
}
public void setComplete(final Boolean complete) {
this.complete = complete;
}
}

View File

@@ -0,0 +1,140 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for DistributionSet for POST.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSetRequestBodyPost extends MgmtDistributionSetRequestBodyPut {
// deprecated format from the time where os, application and runtime where
// statically defined
@JsonProperty
private MgmtSoftwareModuleAssigment os;
@JsonProperty
private MgmtSoftwareModuleAssigment runtime;
@JsonProperty
private MgmtSoftwareModuleAssigment application;
// deprecated format - END
@JsonProperty
private List<MgmtSoftwareModuleAssigment> modules;
@JsonProperty
private String type;
/**
* @return the os
*/
public MgmtSoftwareModuleAssigment getOs() {
return os;
}
/**
* @param os
* the os to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPost setOs(final MgmtSoftwareModuleAssigment os) {
this.os = os;
return this;
}
/**
* @return the runtime
*/
public MgmtSoftwareModuleAssigment getRuntime() {
return runtime;
}
/**
* @param runtime
* the runtime to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPost setRuntime(final MgmtSoftwareModuleAssigment runtime) {
this.runtime = runtime;
return this;
}
/**
* @return the application
*/
public MgmtSoftwareModuleAssigment getApplication() {
return application;
}
/**
* @param application
* the application to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPost setApplication(final MgmtSoftwareModuleAssigment application) {
this.application = application;
return this;
}
/**
* @return the modules
*/
public List<MgmtSoftwareModuleAssigment> getModules() {
return modules;
}
/**
* @param modules
* the modules to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPost setModules(final List<MgmtSoftwareModuleAssigment> modules) {
this.modules = modules;
return this;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type
* the type to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPost setType(final String type) {
this.type = type;
return this;
}
}

View File

@@ -0,0 +1,110 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for DistributionSet for PUT/POST.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSetRequestBodyPut {
@JsonProperty
private String name;
@JsonProperty
private String description;
@JsonProperty
private String version;
@JsonProperty
private Boolean requiredMigrationStep;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPut setName(final String name) {
this.name = name;
return this;
}
/**
* @return the requiredMigrationStep
*/
public Boolean isRequiredMigrationStep() {
return requiredMigrationStep;
}
/**
* @param requiredMigrationStep
* the requiredMigrationStep to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPut setRequiredMigrationStep(final Boolean requiredMigrationStep) {
this.requiredMigrationStep = requiredMigrationStep;
return this;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description
* the description to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPut setDescription(final String description) {
this.description = description;
return this;
}
/**
* @return the version
*/
public String getVersion() {
return version;
}
/**
* @param version
* the version to set
*
* @return updated body
*/
public MgmtDistributionSetRequestBodyPut setVersion(final String version) {
this.version = version;
return this;
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body of Target for assignment operations (ID only).
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetAssignmentRequestBody {
@JsonProperty
private String id;
private long forcetime;
private MgmtActionType type;
/**
* {@link MgmtMaintenanceWindowRequestBody} object containing schedule,
* duration and timezone.
*/
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public MgmtActionType getType() {
return type;
}
public void setType(final MgmtActionType type) {
this.type = type;
}
public long getForcetime() {
return forcetime;
}
public void setForcetime(final long forcetime) {
this.forcetime = forcetime;
}
public MgmtMaintenanceWindowRequestBody getMaintenanceWindow() {
return maintenanceWindow;
}
public void setMaintenanceWindow(final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionset;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Response Body of Target for assignment operations.
*
*
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetAssignmentResponseBody {
private int assigned;
private int alreadyAssigned;
private int total;
/**
* @return the assigned
*/
public int getAssigned() {
return assigned;
}
/**
* @param assigned
* the assigned to set
*/
public void setAssigned(final int assigned) {
this.assigned = assigned;
}
/**
* @return the alreadyAssigned
*/
public int getAlreadyAssigned() {
return alreadyAssigned;
}
/**
* @param alreadyAssigned
* the alreadyAssigned to set
*/
public void setAlreadyAssigned(final int alreadyAssigned) {
this.alreadyAssigned = alreadyAssigned;
}
/**
* @return the total
*/
public int getTotal() {
return total;
}
/**
* @param total
* the total to set
*/
public void setTotal(final int total) {
this.total = total;
}
}

View File

@@ -0,0 +1,77 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionsettype;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for SoftwareModuleType to RESTful API
* representation.
*
*
*
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSetType extends MgmtNamedEntity {
@JsonProperty(value = "id", required = true)
private Long moduleId;
@JsonProperty
private String key;
@JsonProperty
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
/**
* @return the moduleId
*/
public Long getModuleId() {
return moduleId;
}
/**
* @param moduleId
* the moduleId to set
*/
public void setModuleId(final Long moduleId) {
this.moduleId = moduleId;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*/
public void setKey(final String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,121 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionsettype;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleTypeAssigment;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for DistributionSetType POST.
*
*/
public class MgmtDistributionSetTypeRequestBodyPost extends MgmtDistributionSetTypeRequestBodyPut {
@JsonProperty(required = true)
private String name;
@JsonProperty
private String key;
@JsonProperty
private List<MgmtSoftwareModuleTypeAssigment> mandatorymodules;
@JsonProperty
private List<MgmtSoftwareModuleTypeAssigment> optionalmodules;
@Override
public MgmtDistributionSetTypeRequestBodyPost setDescription(final String description) {
super.setDescription(description);
return this;
}
@Override
public MgmtDistributionSetTypeRequestBodyPost setColour(final String colour) {
super.setColour(colour);
return this;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*
* @return updated body
*/
public MgmtDistributionSetTypeRequestBodyPost setName(final String name) {
this.name = name;
return this;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
*
* @return updated body
*/
public MgmtDistributionSetTypeRequestBodyPost setKey(final String key) {
this.key = key;
return this;
}
/**
* @return the mandatory modules
*/
public List<MgmtSoftwareModuleTypeAssigment> getMandatorymodules() {
return mandatorymodules;
}
/**
* @param mandatorymodules
* the mandatory modules to set
*
* @return updated body
*/
public MgmtDistributionSetTypeRequestBodyPost setMandatorymodules(
final List<MgmtSoftwareModuleTypeAssigment> mandatorymodules) {
this.mandatorymodules = mandatorymodules;
return this;
}
/**
* @return the optional modules
*/
public List<MgmtSoftwareModuleTypeAssigment> getOptionalmodules() {
return optionalmodules;
}
/**
* @param optionalmodules
* the optional modules to set
*
* @return updated body
*/
public MgmtDistributionSetTypeRequestBodyPost setOptionalmodules(
final List<MgmtSoftwareModuleTypeAssigment> optionalmodules) {
this.optionalmodules = optionalmodules;
return this;
}
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.distributionsettype;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for DistributionSetType PUT, i.e. update.
*
*/
public class MgmtDistributionSetTypeRequestBodyPut {
@JsonProperty
private String description;
@JsonProperty
private String colour;
public String getDescription() {
return description;
}
public MgmtDistributionSetTypeRequestBodyPut setDescription(final String description) {
this.description = description;
return this;
}
public String getColour() {
return colour;
}
public MgmtDistributionSetTypeRequestBodyPut setColour(final String colour) {
this.colour = colour;
return this;
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Model for defining Conditions and Actions
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class AbstractMgmtRolloutConditionsEntity extends MgmtNamedEntity {
private MgmtRolloutCondition successCondition;
private MgmtRolloutSuccessAction successAction;
private MgmtRolloutCondition errorCondition;
private MgmtRolloutErrorAction errorAction;
public MgmtRolloutCondition getSuccessCondition() {
return successCondition;
}
public void setSuccessCondition(final MgmtRolloutCondition successCondition) {
this.successCondition = successCondition;
}
public MgmtRolloutSuccessAction getSuccessAction() {
return successAction;
}
public void setSuccessAction(final MgmtRolloutSuccessAction successAction) {
this.successAction = successAction;
}
public MgmtRolloutCondition getErrorCondition() {
return errorCondition;
}
public void setErrorCondition(final MgmtRolloutCondition errorCondition) {
this.errorCondition = errorCondition;
}
public MgmtRolloutErrorAction getErrorAction() {
return errorAction;
}
public void setErrorAction(final MgmtRolloutErrorAction errorAction) {
this.errorAction = errorAction;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutCondition {
private Condition condition = Condition.THRESHOLD;
private String expression = "100";
/**
*
*/
public MgmtRolloutCondition() {
// needed for jackson json creator.
}
public MgmtRolloutCondition(final Condition condition, final String expression) {
this.condition = condition;
this.expression = expression;
}
/**
* @return the condition
*/
public Condition getCondition() {
return condition;
}
/**
* @param condition
* the condition to set
*/
public void setCondition(final Condition condition) {
this.condition = condition;
}
/**
* @return the expession
*/
public String getExpression() {
return expression;
}
/**
* @param expession
* the expession to set
*/
public void setExpression(final String expession) {
this.expression = expession;
}
public enum Condition {
THRESHOLD;
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* An action that runs when the error condition is met
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutErrorAction {
private ErrorAction action = ErrorAction.PAUSE;
private String expression;
/**
* Creates a rollout error action
*
* @param action
* the action to run when th error condition is met
* @param expression
* the expression for the action
*/
public MgmtRolloutErrorAction(ErrorAction action, String expression) {
this.action = action;
this.expression = expression;
}
/**
* Default constructor
*/
public MgmtRolloutErrorAction() {
// Instantiate default error action
}
/**
* @return the action
*/
public ErrorAction getAction() {
return action;
}
/**
* @param action
* the action to set
*/
public void setAction(final ErrorAction action) {
this.action = action;
}
/**
* @return the expression
*/
public String getExpression() {
return expression;
}
/**
* @param expression
* the expression to set
*/
public void setExpression(final String expression) {
this.expression = expression;
}
/**
* Possible actions
*/
public enum ErrorAction {
PAUSE;
}
}

View File

@@ -0,0 +1,105 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutResponseBody extends MgmtNamedEntity {
private String targetFilterQuery;
private Long distributionSetId;
@JsonProperty(value = "id", required = true)
private Long rolloutId;
@JsonProperty(required = true)
private String status;
@JsonProperty(required = true)
private Long totalTargets;
@JsonProperty
private Map<String, Long> totalTargetsPerStatus;
@JsonProperty
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
public Long getRolloutId() {
return rolloutId;
}
public void setRolloutId(final Long rolloutId) {
this.rolloutId = rolloutId;
}
public String getTargetFilterQuery() {
return targetFilterQuery;
}
public void setTargetFilterQuery(final String targetFilterQuery) {
this.targetFilterQuery = targetFilterQuery;
}
public Long getDistributionSetId() {
return distributionSetId;
}
public void setDistributionSetId(final Long distributionSetId) {
this.distributionSetId = distributionSetId;
}
public void setTotalTargets(final Long totalTargets) {
this.totalTargets = totalTargets;
}
public Long getTotalTargets() {
return totalTargets;
}
public Map<String, Long> getTotalTargetsPerStatus() {
return totalTargetsPerStatus;
}
public void addTotalTargetsPerStatus(final String status, final Long totalTargetCountByStatus) {
if (totalTargetsPerStatus == null) {
totalTargetsPerStatus = new HashMap<>();
}
totalTargetsPerStatus.put(status, totalTargetCountByStatus);
}
}

View File

@@ -0,0 +1,144 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
import org.eclipse.hawkbit.mgmt.json.model.rolloutgroup.MgmtRolloutGroup;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Model for request containing a rollout body e.g. in a POST request of
* creating a rollout via REST API.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutRestRequestBody extends AbstractMgmtRolloutConditionsEntity {
private String targetFilterQuery;
private long distributionSetId;
private Integer amountGroups;
private Long forcetime;
private Long startAt;
private MgmtActionType type;
private List<MgmtRolloutGroup> groups;
/**
* @return the targetFilterQuery
*/
public String getTargetFilterQuery() {
return targetFilterQuery;
}
/**
* @param targetFilterQuery
* the targetFilterQuery to set
*/
public void setTargetFilterQuery(final String targetFilterQuery) {
this.targetFilterQuery = targetFilterQuery;
}
/**
* @return the distributionSetId
*/
public long getDistributionSetId() {
return distributionSetId;
}
/**
* @param distributionSetId
* the distributionSetId to set
*/
public void setDistributionSetId(final long distributionSetId) {
this.distributionSetId = distributionSetId;
}
/**
* @return the groupSize
*/
public Integer getAmountGroups() {
return amountGroups;
}
/**
* @param groupSize
* the groupSize to set
*/
public void setAmountGroups(final Integer groupSize) {
this.amountGroups = groupSize;
}
/**
* @return the forcetime
*/
public Long getForcetime() {
return forcetime;
}
/**
* @param forcetime
* the forcetime to set
*/
public void setForcetime(final Long forcetime) {
this.forcetime = forcetime;
}
/**
* @return the type
*/
public MgmtActionType getType() {
return type;
}
/**
* @param type
* the type to set
*/
public void setType(final MgmtActionType type) {
this.type = type;
}
/**
* @return the List of defined Groups
*/
public List<MgmtRolloutGroup> getGroups() {
return groups;
}
/**
* @param groups List of {@link MgmtRolloutGroup}
*/
public void setGroups(List<MgmtRolloutGroup> groups) {
this.groups = groups;
}
/**
* @return the start at timestamp in millis or null
*/
public Long getStartAt() {
return startAt;
}
/**
* @param startAt
* the start at timestamp in millis or null
*/
public void setStartAt(Long startAt) {
this.startAt = startAt;
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rollout;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutSuccessAction {
private SuccessAction action = SuccessAction.NEXTGROUP;
private String expression = null;
/**
*
*/
public MgmtRolloutSuccessAction() {
// needed for json creator
}
public MgmtRolloutSuccessAction(final SuccessAction action, final String expression) {
this.action = action;
this.expression = expression;
}
/**
* @return the action
*/
public SuccessAction getAction() {
return action;
}
/**
* @param action
* the action to set
*/
public void setAction(final SuccessAction action) {
this.action = action;
}
/**
* @return the expession
*/
public String getExpression() {
return expression;
}
/**
* @param expession
* the expession to set
*/
public void setExpression(final String expession) {
this.expression = expession;
}
public enum SuccessAction {
NEXTGROUP;
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rolloutgroup;
import org.eclipse.hawkbit.mgmt.json.model.rollout.AbstractMgmtRolloutConditionsEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
/**
* Model for defining the Attributes of a Rollout Group
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutGroup extends AbstractMgmtRolloutConditionsEntity {
private String targetFilterQuery;
private Float targetPercentage;
public String getTargetFilterQuery() {
return targetFilterQuery;
}
public void setTargetFilterQuery(final String targetFilterQuery) {
this.targetFilterQuery = targetFilterQuery;
}
public Float getTargetPercentage() {
return targetPercentage;
}
public void setTargetPercentage(Float targetPercentage) {
this.targetPercentage = targetPercentage;
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.rolloutgroup;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Model for the rollout group annotated with json-annotations for easier
* serialization and de-serialization.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtRolloutGroupResponseBody extends MgmtRolloutGroup {
@JsonProperty(value = "id", required = true)
private Long rolloutGroupId;
@JsonProperty(required = true)
private String status;
private int totalTargets;
private Map<String, Long> totalTargetsPerStatus;
public Long getRolloutGroupId() {
return rolloutGroupId;
}
public void setRolloutGroupId(final Long rolloutGroupId) {
this.rolloutGroupId = rolloutGroupId;
}
public String getStatus() {
return status;
}
public void setStatus(final String status) {
this.status = status;
}
public int getTotalTargets() {
return totalTargets;
}
public void setTotalTargets(final int totalTargets) {
this.totalTargets = totalTargets;
}
public Map<String, Long> getTotalTargetsPerStatus() {
return totalTargetsPerStatus;
}
public void addTotalTargetsPerStatus(final String status, final Long totalTargetCountByStatus) {
if (totalTargetsPerStatus == null) {
totalTargetsPerStatus = new HashMap<>();
}
totalTargetsPerStatus.put(status, totalTargetCountByStatus);
}
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for SoftwareModule to RESTful API representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModule extends MgmtNamedEntity {
@JsonProperty(value = "id", required = true)
private Long moduleId;
@JsonProperty(required = true)
private String version;
@JsonProperty(required = true)
private String type;
@JsonProperty
private String vendor;
@JsonProperty
private boolean deleted;
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public boolean isDeleted() {
return deleted;
}
public Long getModuleId() {
return moduleId;
}
@JsonIgnore
public void setModuleId(final Long moduleId) {
this.moduleId = moduleId;
}
public String getVersion() {
return version;
}
public void setVersion(final String version) {
this.version = version;
}
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
public String getVendor() {
return vendor;
}
public void setVendor(final String vendor) {
this.vendor = vendor;
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Request Body of SoftwareModule for assignment operations (ID only).
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleAssigment extends MgmtId {
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The representation of SoftwareModuleMetadata in the REST API for POST/Create.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleMetadata {
@JsonProperty(required = true)
private String key;
@JsonProperty
private String value;
@JsonProperty
private boolean targetVisible;
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
public boolean isTargetVisible() {
return targetVisible;
}
public void setTargetVisible(final boolean targetVisible) {
this.targetVisible = targetVisible;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The representation of an meta data in the REST API for PUT/Update.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleMetadataBodyPut {
@JsonProperty
private String value;
@JsonProperty
private Boolean targetVisible;
public String getValue() {
return value;
}
public void setValue(final String value) {
this.value = value;
}
public Boolean isTargetVisible() {
return targetVisible;
}
public void setTargetVisible(final Boolean targetVisible) {
this.targetVisible = targetVisible;
}
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for SoftwareModule POST.
*
*/
public class MgmtSoftwareModuleRequestBodyPost {
@JsonProperty(required = true)
private String name;
@JsonProperty(required = true)
private String version;
@JsonProperty(required = true)
private String type;
@JsonProperty
private String description;
@JsonProperty
private String vendor;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPost setName(final String name) {
this.name = name;
return this;
}
/**
* @return the version
*/
public String getVersion() {
return version;
}
/**
* @param version
* the version to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPost setVersion(final String version) {
this.version = version;
return this;
}
/**
* @return the type
*/
public String getType() {
return type;
}
/**
* @param type
* the type to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPost setType(final String type) {
this.type = type;
return this;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description
* the description to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPost setDescription(final String description) {
this.description = description;
return this;
}
/**
* @return the vendor
*/
public String getVendor() {
return vendor;
}
/**
* @param vendor
* the vendor to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPost setVendor(final String vendor) {
this.vendor = vendor;
return this;
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremodule;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for SoftwareModule PUT.
*
*/
public class MgmtSoftwareModuleRequestBodyPut {
@JsonProperty
private String description;
@JsonProperty
private String vendor;
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @param description
* the description to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPut setDescription(final String description) {
this.description = description;
return this;
}
/**
* @return the vendor
*/
public String getVendor() {
return vendor;
}
/**
* @param vendor
* the vendor to set
*
* @return updated body
*/
public MgmtSoftwareModuleRequestBodyPut setVendor(final String vendor) {
this.vendor = vendor;
return this;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for SoftwareModuleType to RESTful API
* representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleType extends MgmtNamedEntity {
@JsonProperty(value = "id", required = true)
private Long moduleId;
@JsonProperty
private String key;
@JsonProperty
private int maxAssignments;
@JsonProperty
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
public Long getModuleId() {
return moduleId;
}
public void setModuleId(final Long moduleId) {
this.moduleId = moduleId;
}
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
public int getMaxAssignments() {
return maxAssignments;
}
public void setMaxAssignments(final int maxAssignments) {
this.maxAssignments = maxAssignments;
}
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Request Body of SoftwareModuleType for assignment operations (ID only).
*
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSoftwareModuleTypeAssigment extends MgmtId {
}

View File

@@ -0,0 +1,93 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for SoftwareModuleType POST.
*
*/
public class MgmtSoftwareModuleTypeRequestBodyPost extends MgmtSoftwareModuleTypeRequestBodyPut {
@JsonProperty(required = true)
private String name;
@JsonProperty
private String key;
@JsonProperty
private int maxAssignments;
@Override
public MgmtSoftwareModuleTypeRequestBodyPost setDescription(final String description) {
super.setDescription(description);
return this;
}
@Override
public MgmtSoftwareModuleTypeRequestBodyPost setColour(final String colour) {
super.setColour(colour);
return this;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*
* @return updated body
*/
public MgmtSoftwareModuleTypeRequestBodyPost setName(final String name) {
this.name = name;
return this;
}
/**
* @return the key
*/
public String getKey() {
return key;
}
/**
* @param key
* the key to set
* @return updated body
*/
public MgmtSoftwareModuleTypeRequestBodyPost setKey(final String key) {
this.key = key;
return this;
}
/**
* @return the maxAssignments
*/
public int getMaxAssignments() {
return maxAssignments;
}
/**
* @param maxAssignments
* the maxAssignments to set
*
* @return updated body
*/
public MgmtSoftwareModuleTypeRequestBodyPost setMaxAssignments(final int maxAssignments) {
this.maxAssignments = maxAssignments;
return this;
}
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for SoftwareModuleType PUT.
*
*/
public class MgmtSoftwareModuleTypeRequestBodyPut {
@JsonProperty
private String description;
@JsonProperty
private String colour;
public String getDescription() {
return description;
}
public MgmtSoftwareModuleTypeRequestBodyPut setDescription(final String description) {
this.description = description;
return this;
}
public String getColour() {
return colour;
}
public MgmtSoftwareModuleTypeRequestBodyPut setColour(final String colour) {
this.colour = colour;
return this;
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.system;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* A json annotated rest model for a tenant configuration value to RESTful API
* representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSystemTenantConfigurationValue extends ResourceSupport {
@JsonInclude(Include.ALWAYS)
private Object value;
@JsonInclude(Include.ALWAYS)
private boolean isGlobal = true;
private Long lastModifiedAt;
private String lastModifiedBy;
private Long createdAt;
private String createdBy;
public Object getValue() {
return value;
}
public void setValue(final Object value) {
this.value = value;
}
public boolean isGlobal() {
return isGlobal;
}
public void setGlobal(final boolean isGlobal) {
this.isGlobal = isGlobal;
}
public Long getLastModifiedAt() {
return lastModifiedAt;
}
public void setLastModifiedAt(final Long lastModifiedAt) {
this.lastModifiedAt = lastModifiedAt;
}
public String getLastModifiedBy() {
return lastModifiedBy;
}
public void setLastModifiedBy(final String lastModifiedBy) {
this.lastModifiedBy = lastModifiedBy;
}
public Long getCreatedAt() {
return createdAt;
}
public void setCreatedAt(final Long createdAt) {
this.createdAt = createdAt;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}
}

View File

@@ -0,0 +1,48 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.system;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for System Configuration for PUT.
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSystemTenantConfigurationValueRequest {
@JsonProperty(required = true)
private Serializable value;
/**
*
* @return the value of the MgmtSystemTenantConfigurationValueRequest
*/
public Serializable getValue() {
return value;
}
/**
* Sets the MgmtSystemTenantConfigurationValueRequest
*
* @param value
*/
public void setValue(final Object value) {
if (!(value instanceof Serializable)) {
throw new IllegalArgumentException("The value muste be a instance of " + Serializable.class.getName());
}
this.value = (Serializable) value;
}
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.systemmanagement;
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
/**
* Model representation of an Cache entry as json.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSystemCache {
private final String name;
private final Collection<String> keys;
/**
* @param name
* the name of the cache
* @param cacheKeys
* the keys which contains in the cache
*/
public MgmtSystemCache(final String name, final Collection<String> cacheKeys) {
this.name = name;
this.keys = cacheKeys;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the keys
*/
public Collection<String> getKeys() {
return keys;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "MgmtSystemCache [name=" + name + ", keys=" + keys + "]";
}
}

View File

@@ -0,0 +1,97 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.systemmanagement;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Body for system statistics.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSystemStatisticsRest {
@JsonProperty
private long overallTargets;
@JsonProperty
private long overallArtifacts;
@JsonProperty
private long overallArtifactVolumeInBytes;
@JsonProperty
private long overallActions;
@JsonProperty
private long overallTenants;
@JsonProperty
private List<MgmtSystemTenantServiceUsage> tenantStats;
public long getOverallTargets() {
return overallTargets;
}
public MgmtSystemStatisticsRest setOverallTargets(final long overallTargets) {
this.overallTargets = overallTargets;
return this;
}
public long getOverallArtifacts() {
return overallArtifacts;
}
public MgmtSystemStatisticsRest setOverallArtifacts(final long overallArtifacts) {
this.overallArtifacts = overallArtifacts;
return this;
}
public long getOverallArtifactVolumeInBytes() {
return overallArtifactVolumeInBytes;
}
public MgmtSystemStatisticsRest setOverallArtifactVolumeInBytes(final long overallArtifactVolumeInBytes) {
this.overallArtifactVolumeInBytes = overallArtifactVolumeInBytes;
return this;
}
public long getOverallActions() {
return overallActions;
}
public MgmtSystemStatisticsRest setOverallActions(final long overallActions) {
this.overallActions = overallActions;
return this;
}
public long getOverallTenants() {
return overallTenants;
}
public MgmtSystemStatisticsRest setOverallTenants(final long overallTenants) {
this.overallTenants = overallTenants;
return this;
}
public void setTenantStats(final List<MgmtSystemTenantServiceUsage> tenantStats) {
this.tenantStats = tenantStats;
}
public List<MgmtSystemTenantServiceUsage> getTenantStats() {
return tenantStats;
}
}

View File

@@ -0,0 +1,92 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.systemmanagement;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Response body for system usage report.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtSystemTenantServiceUsage {
@JsonProperty
private String tenantName;
@JsonProperty
private long targets;
@JsonProperty
private long artifacts;
@JsonProperty
private long actions;
@JsonProperty
private long overallArtifactVolumeInBytes;
@JsonProperty
private Map<String, String> usageData;
public void setTenantName(final String tenantName) {
this.tenantName = tenantName;
}
public long getTargets() {
return targets;
}
public void setTargets(final long targets) {
this.targets = targets;
}
public long getArtifacts() {
return artifacts;
}
public void setArtifacts(final long artifacts) {
this.artifacts = artifacts;
}
public long getActions() {
return actions;
}
public void setActions(final long actions) {
this.actions = actions;
}
public long getOverallArtifactVolumeInBytes() {
return overallArtifactVolumeInBytes;
}
public void setOverallArtifactVolumeInBytes(final long overallArtifactVolumeInBytes) {
this.overallArtifactVolumeInBytes = overallArtifactVolumeInBytes;
}
public String getTenantName() {
return tenantName;
}
public Map<String, String> getUsageData() {
return usageData;
}
public void setUsageData(final Map<String, String> usageData) {
this.usageData = usageData;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for PUT.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtAssignedDistributionSetRequestBody {
@JsonProperty(value = "id", required = true)
private Long distributionSetId;
public Long getDistributionSetId() {
return distributionSetId;
}
public MgmtAssignedDistributionSetRequestBody setDistributionSetId(final Long distributionSetId) {
this.distributionSetId = distributionSetId;
return this;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for PUT.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtAssignedTargetRequestBody {
@JsonProperty(required = true)
private String controllerId;
public String getControllerId() {
return controllerId;
}
public MgmtAssignedTargetRequestBody setControllerId(final String controllerId) {
this.controllerId = controllerId;
return this;
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* * A json annotated rest model for DSAssigmentResult to RESTful API
* representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtDistributionSetTagAssigmentResult {
@JsonProperty
private List<MgmtDistributionSet> assignedDistributionSets;
@JsonProperty
private List<MgmtDistributionSet> unassignedDistributionSets;
public List<MgmtDistributionSet> getAssignedDistributionSets() {
return assignedDistributionSets;
}
public List<MgmtDistributionSet> getUnassignedDistributionSets() {
return unassignedDistributionSets;
}
public void setAssignedDistributionSets(final List<MgmtDistributionSet> assignedDistributionSets) {
this.assignedDistributionSets = assignedDistributionSets;
}
public void setUnassignedDistributionSets(final List<MgmtDistributionSet> unassignedDistributionSets) {
this.unassignedDistributionSets = unassignedDistributionSets;
}
}

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Tag to RESTful API representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTag extends MgmtNamedEntity {
@JsonProperty(value = "id", required = true)
private Long tagId;
@JsonProperty
private String colour;
@JsonIgnore
public void setTagId(final Long tagId) {
this.tagId = tagId;
}
public Long getTagId() {
return tagId;
}
public void setColour(final String colour) {
this.colour = colour;
}
public String getColour() {
return colour;
}
}

View File

@@ -0,0 +1,59 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request Body for PUT/POST.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTagRequestBodyPut {
@JsonProperty
private String colour;
@JsonProperty
private String name;
@JsonProperty
private String description;
public String getName() {
return name;
}
public MgmtTagRequestBodyPut setName(final String name) {
this.name = name;
return this;
}
public String getDescription() {
return description;
}
public MgmtTagRequestBodyPut setDescription(final String description) {
this.description = description;
return this;
}
public MgmtTagRequestBodyPut setColour(final String colour) {
this.colour = colour;
return this;
}
public String getColour() {
return colour;
}
}

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.tag;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* * A json annotated rest model for TargetTagAssigmentResult to RESTful API
* representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetTagAssigmentResult {
@JsonProperty
private List<MgmtTarget> assignedTargets;
@JsonProperty
private List<MgmtTarget> unassignedTargets;
public void setAssignedTargets(final List<MgmtTarget> assignedTargets) {
this.assignedTargets = assignedTargets;
}
public List<MgmtTarget> getAssignedTargets() {
return assignedTargets;
}
public void setUnassignedTargets(final List<MgmtTarget> unassignedTargets) {
this.unassignedTargets = unassignedTargets;
}
public List<MgmtTarget> getUnassignedTargets() {
return unassignedTargets;
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMaintenanceWindowRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
/**
* Request Body of DistributionSet for assignment operations (ID only).
*
*/
public class MgmtDistributionSetAssignment extends MgmtId {
private long forcetime;
private MgmtActionType type;
/**
* {@link MgmtMaintenanceWindowRequestBody} object defining a schedule,
* duration and timezone.
*/
private MgmtMaintenanceWindowRequestBody maintenanceWindow;
public MgmtActionType getType() {
return type;
}
public void setType(final MgmtActionType type) {
this.type = type;
}
public long getForcetime() {
return forcetime;
}
public void setForcetime(final long forcetime) {
this.forcetime = forcetime;
}
/**
* Returns {@link MgmtMaintenanceWindowRequestBody} for distribution set
* assignment.
*
* @return {@link MgmtMaintenanceWindowRequestBody}.
*/
public MgmtMaintenanceWindowRequestBody getMaintenanceWindow() {
return maintenanceWindow;
}
/**
* Sets {@link MgmtMaintenanceWindowRequestBody} for distribution set
* assignment.
*
* @param maintenanceWindow
* as {@link MgmtMaintenanceWindowRequestBody}.
*/
public void setMaintenanceWindow(final MgmtMaintenanceWindowRequestBody maintenanceWindow) {
this.maintenanceWindow = maintenanceWindow;
}
}

View File

@@ -0,0 +1,170 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import java.net.URI;
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
import org.eclipse.hawkbit.mgmt.json.model.MgmtPollStatus;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Target to RESTful API representation.
*
*/
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTarget extends MgmtNamedEntity {
@JsonProperty(required = true)
private String controllerId;
@JsonProperty
private String updateStatus;
@JsonProperty
private Long lastControllerRequestAt;
@JsonProperty
private Long installedAt;
@JsonProperty
private String ipAddress;
@JsonProperty
private String address;
@JsonProperty
private MgmtPollStatus pollStatus;
@JsonProperty
private String securityToken;
/**
* @return the controllerId
*/
public String getControllerId() {
return controllerId;
}
/**
* @param controllerId
* the controllerId to set
*/
public void setControllerId(final String controllerId) {
this.controllerId = controllerId;
}
/**
* @return the updateStatus
*/
public String getUpdateStatus() {
return updateStatus;
}
/**
* @param updateStatus
* the updateStatus to set
*/
public void setUpdateStatus(final String updateStatus) {
this.updateStatus = updateStatus;
}
/**
* @return the lastControllerRequestAt
*/
public Long getLastControllerRequestAt() {
return lastControllerRequestAt;
}
/**
* @param lastControllerRequestAt
* the lastControllerRequestAt to set
*/
@JsonIgnore
public void setLastControllerRequestAt(final Long lastControllerRequestAt) {
this.lastControllerRequestAt = lastControllerRequestAt;
}
/**
* @return the installedAt
*/
public Long getInstalledAt() {
return installedAt;
}
/**
* @param installedAt
* the installedAt to set
*/
@JsonIgnore
public void setInstalledAt(final Long installedAt) {
this.installedAt = installedAt;
}
/**
* @return the pollStatus
*/
public MgmtPollStatus getPollStatus() {
return pollStatus;
}
/**
* @param pollStatus
* the pollStatus to set
*/
@JsonIgnore
public void setPollStatus(final MgmtPollStatus pollStatus) {
this.pollStatus = pollStatus;
}
/**
* @return the ipAddress
*/
public String getIpAddress() {
return ipAddress;
}
/**
* @param ipAddress
* the ipAddress to set
*/
@JsonIgnore
public void setIpAddress(final String ipAddress) {
this.ipAddress = ipAddress;
}
/**
* @return the securityToken
*/
public String getSecurityToken() {
return securityToken;
}
public String getAddress() {
return address;
}
@JsonIgnore
public void setAddress(final String address) {
if (address != null) {
URI.create(address);
}
this.address = address;
}
/**
* @param securityToken
* the securityToken to set
*/
@JsonIgnore
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
}

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import java.util.HashMap;
import java.util.Map;
/**
* {@link Map} with attributes of SP Target.
*/
public class MgmtTargetAttributes extends HashMap<String, String> {
private static final long serialVersionUID = 1L;
}

View File

@@ -0,0 +1,91 @@
/**
* Copyright (c) 2011-2015 Bosch Software Innovations GmbH, Germany. All rights reserved.
*/
package org.eclipse.hawkbit.mgmt.json.model.target;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request body for target PUT/POST commands.
*
*/
public class MgmtTargetRequestBody {
@JsonProperty(required = true)
private String name;
private String description;
@JsonProperty(required = true)
private String controllerId;
@JsonProperty
private String address;
@JsonProperty
private String securityToken;
public String getSecurityToken() {
return securityToken;
}
public void setSecurityToken(final String securityToken) {
this.securityToken = securityToken;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the description
*/
public String getDescription() {
return description;
}
/**
* @return the controllerId
*/
public String getControllerId() {
return controllerId;
}
/**
* @param name
* the name to set
*/
public MgmtTargetRequestBody setName(final String name) {
this.name = name;
return this;
}
/**
* @param description
* the description to set
*/
public MgmtTargetRequestBody setDescription(final String description) {
this.description = description;
return this;
}
/**
* @param controllerId
* the controllerId to set
*/
public MgmtTargetRequestBody setControllerId(final String controllerId) {
this.controllerId = controllerId;
return this;
}
public String getAddress() {
return address;
}
public void setAddress(final String address) {
this.address = address;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A json annotated rest model for Target Filter Queries to RESTful API
* representation.
*
*/
@JsonInclude(Include.ALWAYS)
@JsonIgnoreProperties(ignoreUnknown = true)
public class MgmtTargetFilterQuery extends MgmtBaseEntity {
@JsonProperty(value = "id", required = true)
private Long filterId;
@JsonProperty
private String name;
@JsonProperty
private String query;
@JsonProperty
private Long autoAssignDistributionSet;
public Long getFilterId() {
return filterId;
}
public void setFilterId(final Long filterId) {
this.filterId = filterId;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getQuery() {
return query;
}
public void setQuery(final String query) {
this.query = query;
}
public Long getAutoAssignDistributionSet() {
return autoAssignDistributionSet;
}
public void setAutoAssignDistributionSet(final Long autoAssignDistributionSet) {
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.json.model.targetfilter;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Request body for target PUT/POST commands.
*
*/
public class MgmtTargetFilterQueryRequestBody {
@JsonProperty(required = true)
private String name;
@JsonProperty(required = true)
private String query;
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}

View File

@@ -0,0 +1,384 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModuleAssigment;
import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget;
import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* REST Resource handling for DistributionSet CRUD operations.
*/
@RequestMapping(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING)
public interface MgmtDistributionSetRestApi {
/**
* Handles the GET request of retrieving all DistributionSets .
*
* @param pagingOffsetParam
* the offset of list of sets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
* @return a list of all set for a defined or default page request with
* status OK. The response is always paged. In any failure the
* JsonResponseExceptionHandler is handling the response.
*/
@RequestMapping(method = RequestMethod.GET, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtDistributionSet>> getDistributionSets(
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving a single DistributionSet .
*
* @param distributionSetId
* the ID of the set to retrieve
*
* @return a single DistributionSet with status OK.
*
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}", produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> getDistributionSet(@PathVariable("distributionSetId") Long distributionSetId);
/**
* Handles the POST request of creating new distribution sets . The request
* body must always be a list of sets.
*
* @param sets
* the DistributionSets to be created.
* @return In case all sets could successful created the ResponseEntity with
* status code 201 - Created but without ResponseBody. In any
* failure the JsonResponseExceptionHandler is handling the
* response.
*/
@RequestMapping(method = RequestMethod.POST, consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> createDistributionSets(List<MgmtDistributionSetRequestBodyPost> sets);
/**
* Handles the DELETE request for a single DistributionSet .
*
* @param distributionSetId
* the ID of the DistributionSet to delete
* @return status OK if delete as successful.
*
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetId}")
ResponseEntity<Void> deleteDistributionSet(@PathVariable("distributionSetId") Long distributionSetId);
/**
* Handles the UPDATE request for a single DistributionSet .
*
* @param distributionSetId
* the ID of the DistributionSet to delete
* @param toUpdate
* with the data that needs updating
*
* @return status OK if update as successful with updated content.
*
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{distributionSetId}", consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE,
MediaTypes.HAL_JSON_VALUE })
ResponseEntity<MgmtDistributionSet> updateDistributionSet(@PathVariable("distributionSetId") Long distributionSetId,
MgmtDistributionSetRequestBodyPut toUpdate);
/**
* Handles the GET request of retrieving assigned targets to a specific
* distribution set.
*
* @param distributionSetId
* the ID of the distribution set to retrieve the assigned
* targets
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
* @return status OK if get request is successful with the paged list of
* targets
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/assignedTargets", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtTarget>> getAssignedTargets(@PathVariable("distributionSetId") Long distributionSetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving installed targets to a specific
* distribution set.
*
* @param distributionSetId
* the ID of the distribution set to retrieve the assigned
* targets
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
* @return status OK if get request is successful with the paged list of
* targets
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/installedTargets", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtTarget>> getInstalledTargets(@PathVariable("distributionSetId") Long distributionSetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request to retrieve target filter queries that have the
* given distribution set as auto assign DS.
*
* @param distributionSetId
* the ID of the distribution set to retrieve the assigned
* targets
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search name parameter in the request URL, syntax
* {@code q=myFilter}
* @return status OK if get request is successful with the paged list of
* targets
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/autoAssignTargetFilters", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtTargetFilterQuery>> getAutoAssignTargetFilterQueries(
@PathVariable("distributionSetId") Long distributionSetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the POST request of assigning multiple targets to a single
* distribution set.
*
* @param distributionSetId
* the ID of the distribution set within the URL path parameter
* @param targetIds
* the IDs of the target which should get assigned to the
* distribution set given in the response body
* @param offline
* to <code>true</code> if update was executed offline, i.e. not
* managed by hawkBit.
* @return status OK if the assignment of the targets was successful and a
* complex return body which contains information about the assigned
* targets and the already assigned targets counters
*/
@RequestMapping(method = RequestMethod.POST, value = "/{distributionSetId}/assignedTargets", consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTargetAssignmentResponseBody> createAssignedTarget(
@PathVariable("distributionSetId") Long distributionSetId,
final List<MgmtTargetAssignmentRequestBody> targetIds,
@RequestParam(value = "offline", required = false) boolean offline);
/**
* Gets a paged list of meta data for a distribution set.
*
* @param distributionSetId
* the ID of the distribution set for the meta data
* @param pagingOffsetParam
* the offset of list of targets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=key==abc}
* @return status OK if get request is successful with the paged list of
* meta data
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/metadata", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtMetadata>> getMetadata(
@PathVariable("distributionSetId") Long distributionSetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Gets a single meta data value for a specific key of a distribution set.
*
* @param distributionSetId
* the ID of the distribution set to get the meta data from
* @param metadataKey
* the key of the meta data entry to retrieve the value from
* @return status OK if get request is successful with the value of the meta
* data
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/metadata/{metadataKey}", produces = {
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtMetadata> getMetadataValue(@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("metadataKey") String metadataKey);
/**
* Updates a single meta data value of a distribution set.
*
* @param distributionSetId
* the ID of the distribution set to update the meta data entry
* @param metadataKey
* the key of the meta data to update the value
* @param metadata
* update body
* @return status OK if the update request is successful and the updated
* meta data result
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{distributionSetId}/metadata/{metadataKey}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtMetadata> updateMetadata(@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("metadataKey") String metadataKey, MgmtMetadataBodyPut metadata);
/**
* Deletes a single meta data entry from the distribution set.
*
* @param distributionSetId
* the ID of the distribution set to delete the meta data entry
* @param metadataKey
* the key of the meta data to delete
* @return status OK if the delete request is successful
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetId}/metadata/{metadataKey}")
ResponseEntity<Void> deleteMetadata(@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("metadataKey") String metadataKey);
/**
* Creates a list of meta data for a specific distribution set.
*
* @param distributionSetId
* the ID of the distribution set to create meta data for
* @param metadataRest
* the list of meta data entries to create
* @return status created if post request is successful with the value of
* the created meta data
*/
@RequestMapping(method = RequestMethod.POST, value = "/{distributionSetId}/metadata", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaTypes.HAL_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtMetadata>> createMetadata(@PathVariable("distributionSetId") Long distributionSetId,
List<MgmtMetadata> metadataRest);
/**
* Assigns a list of software modules to a distribution set.
*
* @param distributionSetId
* the ID of the distribution set to assign software modules for
* @param softwareModuleIDs
* the list of software modules ids to assign
* @return http status
*
*/
@RequestMapping(method = RequestMethod.POST, value = "/{distributionSetId}/assignedSM", consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaTypes.HAL_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> assignSoftwareModules(@PathVariable("distributionSetId") Long distributionSetId,
List<MgmtSoftwareModuleAssigment> softwareModuleIDs);
/**
* Deletes the assignment of the software module form the distribution set.
*
* @param distributionSetId
* the ID of the distribution set to reject the software module
* for
* @param softwareModuleId
* the software module id to get rejected form the distribution
* set
* @return status OK if rejection was successful.
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetId}/assignedSM/{softwareModuleId}")
ResponseEntity<Void> deleteAssignSoftwareModules(@PathVariable("distributionSetId") Long distributionSetId,
@PathVariable("softwareModuleId") Long softwareModuleId);
/**
* Handles the GET request for retrieving the assigned software modules of a
* specific distribution set.
*
* @param distributionSetId
* the ID of the distribution to retrieve
* @param pagingOffsetParam
* the offset of list of sets for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @return a list of the assigned software modules of a distribution set
* with status OK, if none is assigned than {@code null}
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetId}/assignedSM", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtSoftwareModule>> getAssignedSoftwareModules(
@PathVariable("distributionSetId") Long distributionSetId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam);
}

View File

@@ -0,0 +1,277 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtAssignedDistributionSetRequestBody;
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtDistributionSetTagAssigmentResult;
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag;
import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTagRequestBodyPut;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* REST Resource handling for DistributionSetTag CRUD operations.
*
*/
@RequestMapping(MgmtRestConstants.DISTRIBUTIONSET_TAG_V1_REQUEST_MAPPING)
public interface MgmtDistributionSetTagRestApi {
/**
* Handles the GET request of retrieving all DistributionSet tags.
*
* @param pagingOffsetParam
* the offset of list of DistributionSet tags for pagination,
* might not be present in the rest request then default value
* will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
* @return a list of all target tags for a defined or default page request
* with status OK. The response is always paged. In any failure the
* JsonResponseExceptionHandler is handling the response.
*/
@RequestMapping(method = RequestMethod.GET, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtTag>> getDistributionSetTags(
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving a single distribution set tag.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
*
* @return a single distribution set tag with status OK.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionsetTagId}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTag> getDistributionSetTag(@PathVariable("distributionsetTagId") Long distributionsetTagId);
/**
* Handles the POST request of creating new distribution set tag. The
* request body must always be a list of tags.
*
* @param tags
* the distribution set tags to be created.
* @return In case all modules could successful created the ResponseEntity
* with status code 201 - Created. The Response Body are the created
* distribution set tags but without ResponseBody.
*/
@RequestMapping(method = RequestMethod.POST, consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtTag>> createDistributionSetTags(List<MgmtTagRequestBodyPut> tags);
/**
*
* Handles the PUT request of updating a single distribution set tag.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @param restDSTagRest
* the the request body to be updated
* @return status OK if update is successful and the updated distribution
* set tag.
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{distributionsetTagId}", consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtTag> updateDistributionSetTag(@PathVariable("distributionsetTagId") Long distributionsetTagId,
MgmtTagRequestBodyPut restDSTagRest);
/**
* Handles the DELETE request for a single distribution set tag.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @return status OK if delete as successfully.
*
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionsetTagId}")
ResponseEntity<Void> deleteDistributionSetTag(@PathVariable("distributionsetTagId") Long distributionsetTagId);
/**
* Handles the GET request of retrieving all assigned distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @param pagingOffsetParam
* the offset of list of target tags for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
*
* @return the list of assigned distribution sets.
*/
@RequestMapping(method = RequestMethod.GET, value = MgmtRestConstants.DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtDistributionSet>> getAssignedDistributionSets(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving all assigned distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
*
* @return the list of assigned distribution sets.
*
* @deprecated please use
* {@link #getAssignedDistributionSets(Long, int, int, String, String)}
* instead as this variant does not include paging and as result
* returns only a limited list of distributionsets
*/
@Deprecated
@RequestMapping(method = RequestMethod.GET, value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> getAssignedDistributionSets(
@PathVariable("distributionsetTagId") Long distributionsetTagId);
/**
* Handles the POST request to toggle the assignment of distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution set ids to be toggled
*
* @return the list of assigned distribution sets and unassigned
* distribution sets.
*/
@RequestMapping(method = RequestMethod.POST, value = MgmtRestConstants.DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/toggleTagAssignment")
ResponseEntity<MgmtDistributionSetTagAssigmentResult> toggleTagAssignment(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to toggle the assignment of distribution sets by
* the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution set ids to be toggled
*
* @return the list of assigned distribution sets and unassigned
* distribution sets.
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#toggleTagAssignment}
*/
@Deprecated
@RequestMapping(method = RequestMethod.POST, value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/toggleTagAssignment")
ResponseEntity<MgmtDistributionSetTagAssigmentResult> toggleTagAssignmentUnpaged(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to assign distribution sets to the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution sets ids to be assigned
*
* @return the list of assigned distribution set.
*/
@RequestMapping(method = RequestMethod.POST, value = MgmtRestConstants.DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> assignDistributionSets(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the POST request to assign distribution sets to the given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag to retrieve
* @param assignedDSRequestBodies
* list of distribution sets ids to be assigned
*
* @return the list of assigned distribution set.
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#assignDistributionSets}
*/
@Deprecated
@RequestMapping(method = RequestMethod.POST, value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING, consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSet>> assignDistributionSetsUnpaged(
@PathVariable("distributionsetTagId") Long distributionsetTagId,
List<MgmtAssignedDistributionSetRequestBody> assignedDSRequestBodies);
/**
* Handles the DELETE request to unassign one distribution set from the
* given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @param distributionsetId
* the ID of the distribution set to unassign
* @return http status code
*/
@RequestMapping(method = RequestMethod.DELETE, value = MgmtRestConstants.DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/{distributionsetId}")
ResponseEntity<Void> unassignDistributionSet(@PathVariable("distributionsetTagId") Long distributionsetTagId,
@PathVariable("distributionsetId") Long distributionsetId);
/**
* Handles the DELETE request to unassign one distribution set from the
* given tag id.
*
* @param distributionsetTagId
* the ID of the distribution set tag
* @param distributionsetId
* the ID of the distribution set to unassign
* @return http status code
*
* @deprecated please use
* {@link MgmtDistributionSetTagRestApi#unassignDistributionSet}
*/
@Deprecated
@RequestMapping(method = RequestMethod.DELETE, value = MgmtRestConstants.DEPRECATED_DISTRIBUTIONSET_TAG_DISTRIBUTIONSETS_REQUEST_MAPPING
+ "/{distributionsetId}")
ResponseEntity<Void> unassignDistributionSetUnpaged(@PathVariable("distributionsetTagId") Long distributionsetTagId,
@PathVariable("distributionsetId") Long distributionsetId);
}

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.util.List;
import org.eclipse.hawkbit.mgmt.json.model.MgmtId;
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetType;
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPost;
import org.eclipse.hawkbit.mgmt.json.model.distributionsettype.MgmtDistributionSetTypeRequestBodyPut;
import org.eclipse.hawkbit.mgmt.json.model.softwaremoduletype.MgmtSoftwareModuleType;
import org.springframework.hateoas.MediaTypes;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* REST Resource handling for SoftwareModule and related Artifact CRUD
* operations.
*
*/
@RequestMapping(MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_REQUEST_MAPPING)
public interface MgmtDistributionSetTypeRestApi {
/**
* Handles the GET request of retrieving all DistributionSetTypes.
*
* @param pagingOffsetParam
* the offset of list of modules for pagination, might not be
* present in the rest request then default value will be applied
* @param pagingLimitParam
* the limit of the paged request, might not be present in the
* rest request then default value will be applied
* @param sortParam
* the sorting parameter in the request URL, syntax
* {@code field:direction, field:direction}
* @param rsqlParam
* the search parameter in the request URL, syntax
* {@code q=name==abc}
*
* @return a list of all DistributionSetType for a defined or default page
* request with status OK. The response is always paged. In any
* failure the JsonResponseExceptionHandler is handling the
* response.
*/
@RequestMapping(method = RequestMethod.GET, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<PagedList<MgmtDistributionSetType>> getDistributionSetTypes(
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam,
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) String rsqlParam);
/**
* Handles the GET request of retrieving a single DistributionSetType
* within.
*
* @param distributionSetTypeId
* the ID of the module type to retrieve
*
* @return a single softwareModule with status OK.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetTypeId}", produces = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSetType> getDistributionSetType(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId);
/**
* Handles the DELETE request for a single Distribution Set Type.
*
* @param distributionSetTypeId
* the ID of the module to retrieve
* @return status OK if delete as sucessfull.
*
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetTypeId}")
ResponseEntity<Void> deleteDistributionSetType(@PathVariable("distributionSetTypeId") Long distributionSetTypeId);
/**
* Handles the PUT request of updating a Distribution Set Type.
*
* @param distributionSetTypeId
* the ID of the software module in the URL
* @param restDistributionSetType
* the module type to be updated.
* @return status OK if update is successful
*/
@RequestMapping(method = RequestMethod.PUT, value = "/{distributionSetTypeId}", consumes = {
MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtDistributionSetType> updateDistributionSetType(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
MgmtDistributionSetTypeRequestBodyPut restDistributionSetType);
/**
* Handles the POST request of creating new DistributionSetTypes. The
* request body must always be a list of types.
*
* @param distributionSetTypes
* the modules to be created.
* @return In case all modules could successful created the ResponseEntity
* with status code 201 - Created but without ResponseBody. In any
* failure the JsonResponseExceptionHandler is handling the
* response.
*/
@RequestMapping(method = RequestMethod.POST, consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtDistributionSetType>> createDistributionSetTypes(
List<MgmtDistributionSetTypeRequestBodyPost> distributionSetTypes);
/**
* Handles the GET request of retrieving the list of mandatory software
* module types in that distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @return Unpaged list of module types and OK in case of success.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_MANDATORY_MODULE_TYPES, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtSoftwareModuleType>> getMandatoryModules(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId);
/**
* Handles the GET request of retrieving the single mandatory software
* module type in that distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param softwareModuleTypeId
* of SoftwareModuleType.
* @return Unpaged list of module types and OK in case of success.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_MANDATORY_MODULE_TYPES
+ "/{softwareModuleTypeId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtSoftwareModuleType> getMandatoryModule(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
@PathVariable("softwareModuleTypeId") Long softwareModuleTypeId);
/**
* Handles the GET request of retrieving the single optional software module
* type in that distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param softwareModuleTypeId
* of SoftwareModuleType.
* @return Unpaged list of module types and OK in case of success.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_OPTIONAL_MODULE_TYPES
+ "/{softwareModuleTypeId}", produces = { MediaTypes.HAL_JSON_VALUE, MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<MgmtSoftwareModuleType> getOptionalModule(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
@PathVariable("softwareModuleTypeId") Long softwareModuleTypeId);
/**
* Handles the GET request of retrieving the list of optional software
* module types in that distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @return Unpaged list of module types and OK in case of success.
*/
@RequestMapping(method = RequestMethod.GET, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_OPTIONAL_MODULE_TYPES, produces = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<List<MgmtSoftwareModuleType>> getOptionalModules(
@PathVariable("distributionSetTypeId") Long distributionSetTypeId);
/**
* Handles DELETE request for removing a mandatory module from the
* DistributionSetType.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param softwareModuleTypeId
* of the SoftwareModuleType to remove
*
* @return OK if the request was successful
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_MANDATORY_MODULE_TYPES + "/{softwareModuleTypeId}")
ResponseEntity<Void> removeMandatoryModule(@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
@PathVariable("softwareModuleTypeId") Long softwareModuleTypeId);
/**
* Handles DELETE request for removing an optional module from the
* DistributionSetType.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param softwareModuleTypeId
* of the SoftwareModuleType to remove
*
* @return OK if the request was successful
*/
@RequestMapping(method = RequestMethod.DELETE, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_OPTIONAL_MODULE_TYPES + "/{softwareModuleTypeId}")
ResponseEntity<Void> removeOptionalModule(@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
@PathVariable("softwareModuleTypeId") Long softwareModuleTypeId);
/**
* Handles the POST request for adding a mandatory software module type to a
* distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param smtId
* of the SoftwareModuleType to add
*
* @return OK if the request was successful
*/
@RequestMapping(method = RequestMethod.POST, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_MANDATORY_MODULE_TYPES, consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> addMandatoryModule(@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
MgmtId smtId);
/**
* Handles the POST request for adding an optional software module type to a
* distribution set type.
*
* @param distributionSetTypeId
* of the DistributionSetType.
* @param smtId
* of the SoftwareModuleType to add
*
* @return OK if the request was successful
*/
@RequestMapping(method = RequestMethod.POST, value = "/{distributionSetTypeId}/"
+ MgmtRestConstants.DISTRIBUTIONSETTYPE_V1_OPTIONAL_MODULE_TYPES, consumes = { MediaTypes.HAL_JSON_VALUE,
MediaType.APPLICATION_JSON_VALUE })
ResponseEntity<Void> addOptionalModule(@PathVariable("distributionSetTypeId") Long distributionSetTypeId,
MgmtId smtId);
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.io.InputStream;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
*
*/
@RequestMapping(MgmtRestConstants.SOFTWAREMODULE_V1_REQUEST_MAPPING)
@FunctionalInterface
public interface MgmtDownloadArtifactRestApi {
/**
* Handles the GET request for downloading an artifact.
*
* @param softwareModuleId
* of the parent SoftwareModule
* @param artifactId
* of the related LocalArtifact
* @param servletResponse
* of the servlet
* @param request
* of the client
*
* @return responseEntity with status ok if successful
*/
@RequestMapping(method = RequestMethod.GET, value = "/{softwareModuleId}/artifacts/{artifactId}/download")
@ResponseBody
ResponseEntity<InputStream> downloadArtifact(@PathVariable("softwareModuleId") Long softwareModuleId,
@PathVariable("artifactId") Long artifactId);
}

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.mgmt.rest.api;
import java.io.InputStream;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* A resource for download artifacts.
*
*/
@RequestMapping(MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING_BASE)
@FunctionalInterface
public interface MgmtDownloadRestApi {
/**
* Handles the GET request for downloading an artifact.
*
* @param tenant
* the download belongs to
* @param downloadId
* the generated download id
* @param response
* of the servlet
* @return {@link ResponseEntity} with status {@link HttpStatus#OK} if
* successful
*/
@RequestMapping(method = RequestMethod.GET, value = MgmtRestConstants.DOWNLOAD_ID_V1_REQUEST_MAPPING)
@ResponseBody
ResponseEntity<InputStream> downloadArtifactByDownloadId(@PathVariable("tenant") String tenant,
@PathVariable("downloadId") String downloadId);
}

Some files were not shown because too many files have changed in this diff Show More