Protection against misuse with system-wide quota definitions (#670)
* Added quota for meta data per software module Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Added unit test for "meta data per module" quota Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * quota test enhancements Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Verify enforcement of meta data quota via REST Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota for distribution set meta data Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Verify enforcement of distribution set meta data quota via REST Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * software modules per distribution set quota Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Integration test enhancements for Modules per DistSet quota Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota for software module types per distribution set type Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota for max artifacts per software module Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quotas for ActionStatus per Action and Messages per ActionStatus Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota attributes per target Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota targets per rollout group Signed-off-by: stefbehl <stefan.behl@bosch-si.com> * Quota max targets per rollout group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per rollout group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per rollout group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per rollout group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * quota max actions per target Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per rollout group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max actions per target Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max actions per target Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per auto assignment Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per manual assignment Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per auto assignment Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per auto assign Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per auto assignment Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max actions per target Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per manual assignment Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix issues caused by merge Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Improve JavaDoc Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar issues Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max artifact size Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Optimize quota configuration Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix test failures Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Improve test coverage Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max rollout groups per rollout Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Configure Rollout UI enhancements Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * UI enhancements Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Minor changes Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Quota max targets per group Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar finding Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix code review findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix review findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * New approach for 'max artifact size' enforcement Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Sonar findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix failing tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Reduce max artifact size for tests Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com> * Fix Kai's review findings Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
committed by
Kai Zimmermann
parent
fcc15a0484
commit
6dd98d2134
@@ -29,7 +29,6 @@ import com.google.common.io.ByteStreams;
|
|||||||
/**
|
/**
|
||||||
* Abstract utility class for ArtifactRepository implementations with common
|
* Abstract utility class for ArtifactRepository implementations with common
|
||||||
* functionality, e.g. computation of hashes.
|
* functionality, e.g. computation of hashes.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractArtifactRepository implements ArtifactRepository {
|
public abstract class AbstractArtifactRepository implements ArtifactRepository {
|
||||||
|
|
||||||
@@ -71,7 +70,7 @@ public abstract class AbstractArtifactRepository implements ArtifactRepository {
|
|||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new ArtifactStoreException(e.getMessage(), e);
|
throw new ArtifactStoreException(e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
if (file.exists() && !file.delete()) {
|
if (file != null && file.exists() && !file.delete()) {
|
||||||
LOG.error("Could not delete temp file {}", file);
|
LOG.error("Could not delete temp file {}", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
|||||||
@SpringApplicationConfiguration(classes = DownloadTestConfiguration.class)
|
@SpringApplicationConfiguration(classes = DownloadTestConfiguration.class)
|
||||||
public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
||||||
|
|
||||||
private static final int ARTIFACT_SIZE = 5 * 1024 * 1024;
|
|
||||||
|
|
||||||
private static volatile int downLoadProgress = 0;
|
private static volatile int downLoadProgress = 0;
|
||||||
private static volatile long shippedBytes = 0;
|
private static volatile long shippedBytes = 0;
|
||||||
|
|
||||||
@@ -84,9 +82,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
assignDistributionSet(ds, targets);
|
assignDistributionSet(ds, targets);
|
||||||
|
|
||||||
// create artifact
|
// create artifact
|
||||||
final byte random[] = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
||||||
ds.findFirstModuleByType(osType).get().getId(), "file1", false);
|
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
|
||||||
|
|
||||||
// no artifact available
|
// no artifact available
|
||||||
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455",
|
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455",
|
||||||
@@ -170,9 +169,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
|
|
||||||
// create artifact
|
// create artifact
|
||||||
final byte random[] = RandomUtils.nextBytes(ARTIFACT_SIZE);
|
final int artifactSize = 5 * 1024 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
||||||
ds.findFirstModuleByType(osType).get().getId(), "file1", false);
|
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
|
||||||
|
|
||||||
// download fails as artifact is not yet assigned
|
// download fails as artifact is not yet assigned
|
||||||
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
@@ -180,11 +180,9 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
|
|
||||||
// now assign and download successful
|
// now assign and download successful
|
||||||
assignDistributionSet(ds, targets);
|
assignDistributionSet(ds, targets);
|
||||||
final MvcResult result = mvc
|
final MvcResult result = mvc.perform(get(
|
||||||
.perform(
|
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds),
|
|
||||||
artifact.getFilename()))
|
|
||||||
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
.andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||||
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
|
.andExpect(header().string("Last-Modified", dateFormat.format(new Date(artifact.getCreatedAt()))))
|
||||||
@@ -196,7 +194,7 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
|
|
||||||
// download complete
|
// download complete
|
||||||
assertThat(downLoadProgress).isEqualTo(10);
|
assertThat(downLoadProgress).isEqualTo(10);
|
||||||
assertThat(shippedBytes).isEqualTo(ARTIFACT_SIZE);
|
assertThat(shippedBytes).isEqualTo(artifactSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -211,16 +209,15 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
assignDistributionSet(ds, target);
|
assignDistributionSet(ds, target);
|
||||||
|
|
||||||
// create artifact
|
// create artifact
|
||||||
final byte random[] = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
|
||||||
false);
|
false, artifactSize);
|
||||||
|
|
||||||
// download
|
// download
|
||||||
final MvcResult result = mvc
|
final MvcResult result = mvc.perform(get(
|
||||||
.perform(
|
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), artifact.getFilename()))
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds),
|
|
||||||
artifact.getFilename()))
|
|
||||||
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
|
.andExpect(status().isOk()).andExpect(header().string("Content-Disposition",
|
||||||
"attachment;filename=" + artifact.getFilename() + ".MD5SUM"))
|
"attachment;filename=" + artifact.getFilename() + ".MD5SUM"))
|
||||||
.andReturn();
|
.andReturn();
|
||||||
@@ -245,7 +242,7 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
// create artifact
|
// create artifact
|
||||||
final byte random[] = RandomUtils.nextBytes(resultLength);
|
final byte random[] = RandomUtils.nextBytes(resultLength);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "file1",
|
||||||
false);
|
false, resultLength);
|
||||||
|
|
||||||
assertThat(random.length).isEqualTo(resultLength);
|
assertThat(random.length).isEqualTo(resultLength);
|
||||||
|
|
||||||
@@ -259,11 +256,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
for (int i = 0; i < resultLength / range; i++) {
|
for (int i = 0; i < resultLength / range; i++) {
|
||||||
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
|
final String rangeString = "" + i * range + "-" + ((i + 1) * range - 1);
|
||||||
|
|
||||||
final MvcResult result = mvc
|
final MvcResult result = mvc.perform(get(
|
||||||
.perform(
|
"/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1").header("Range",
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
"bytes=" + rangeString))
|
||||||
.header("Range", "bytes=" + rangeString))
|
|
||||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||||
@@ -278,11 +274,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
assertThat(outputStream.toByteArray()).isEqualTo(random);
|
assertThat(outputStream.toByteArray()).isEqualTo(random);
|
||||||
|
|
||||||
// return last 1000 Bytes
|
// return last 1000 Bytes
|
||||||
MvcResult result = mvc
|
MvcResult result = mvc.perform(
|
||||||
.perform(
|
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
.header("Range", "bytes=-1000"))
|
||||||
.header("Range", "bytes=-1000"))
|
|
||||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||||
@@ -296,11 +291,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
.isEqualTo(Arrays.copyOfRange(random, resultLength - 1000, resultLength));
|
.isEqualTo(Arrays.copyOfRange(random, resultLength - 1000, resultLength));
|
||||||
|
|
||||||
// skip first 1000 Bytes and return the rest
|
// skip first 1000 Bytes and return the rest
|
||||||
result = mvc
|
result = mvc.perform(
|
||||||
.perform(
|
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
.header("Range", "bytes=1000-"))
|
||||||
.header("Range", "bytes=1000-"))
|
|
||||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
.andExpect(content().contentType(MediaType.APPLICATION_OCTET_STREAM))
|
||||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||||
@@ -326,11 +320,10 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest {
|
|||||||
.andExpect(header().string("Content-Disposition", "attachment;filename=file1"));
|
.andExpect(header().string("Content-Disposition", "attachment;filename=file1"));
|
||||||
|
|
||||||
// multipart download - first 20 bytes in 2 parts
|
// multipart download - first 20 bytes in 2 parts
|
||||||
result = mvc
|
result = mvc.perform(
|
||||||
.perform(
|
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
||||||
get("/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}",
|
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
||||||
tenantAware.getCurrentTenant(), target.getControllerId(), getOsModule(ds), "file1")
|
.header("Range", "bytes=0-9,10-19"))
|
||||||
.header("Range", "bytes=0-9,10-19"))
|
|
||||||
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
.andExpect(status().isPartialContent()).andExpect(header().string("ETag", artifact.getSha1Hash()))
|
||||||
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))
|
.andExpect(content().contentType("multipart/byteranges; boundary=THIS_STRING_SEPARATES_MULTIPART"))
|
||||||
.andExpect(header().string("Accept-Ranges", "bytes"))
|
.andExpect(header().string("Accept-Ranges", "bytes"))
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
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.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||||
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
||||||
@@ -109,13 +111,13 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
|
@Description("We verify that the config data (i.e. device attributes like serial number, hardware revision etc.) "
|
||||||
+ "upload limitation is inplace which is meant to protect the server from malicious attempts.")
|
+ "upload quota is enforced to protect the server from malicious attempts.")
|
||||||
public void putToMuchConfigData() throws Exception {
|
public void putTooMuchConfigData() throws Exception {
|
||||||
testdataFactory.createTarget("4717");
|
testdataFactory.createTarget("4717");
|
||||||
|
|
||||||
// initial
|
// initial
|
||||||
Map<String, String> attributes = new HashMap<>();
|
Map<String, String> attributes = new HashMap<>();
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < quotaManagement.getMaxAttributeEntriesPerTarget(); i++) {
|
||||||
attributes.put("dsafsdf" + i, "sdsds" + i);
|
attributes.put("dsafsdf" + i, "sdsds" + i);
|
||||||
}
|
}
|
||||||
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
|
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
|
||||||
@@ -126,7 +128,9 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
|||||||
attributes.put("on too many", "sdsds");
|
attributes.put("on too many", "sdsds");
|
||||||
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
|
mvc.perform(put("/{tenant}/controller/v1/4717/configData", tenantAware.getCurrentTenant())
|
||||||
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
|
.content(JsonBuilder.configData("", attributes, "closed")).contentType(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isForbidden());
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +196,7 @@ public class DdiConfigDataTest extends AbstractDDiApiIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Step
|
@Step
|
||||||
private void putConfigDataWithInvalidUpdateMode(final String configDataPath)
|
private void putConfigDataWithInvalidUpdateMode(final String configDataPath) throws Exception {
|
||||||
throws Exception {
|
|
||||||
|
|
||||||
// create some attriutes
|
// create some attriutes
|
||||||
final Map<String, String> attributes = new HashMap<>();
|
final Map<String, String> attributes = new HashMap<>();
|
||||||
|
|||||||
@@ -110,11 +110,12 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
||||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
|
||||||
|
|
||||||
final byte random[] = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
||||||
false);
|
false, artifactSize);
|
||||||
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
||||||
"test1.signature", false);
|
"test1.signature", false, artifactSize);
|
||||||
|
|
||||||
final Target savedTarget = testdataFactory.createTarget("4712");
|
final Target savedTarget = testdataFactory.createTarget("4712");
|
||||||
|
|
||||||
@@ -198,10 +199,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1].hashes.sha1",
|
||||||
contains(artifactSignature.getSha1Hash())))
|
contains(artifactSignature.getSha1Hash())))
|
||||||
|
|
||||||
.andExpect(
|
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
|
||||||
jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.download-http.href",
|
contains(
|
||||||
contains(HTTP_LOCALHOST + tenantAware.getCurrentTenant()
|
HTTP_LOCALHOST + tenantAware.getCurrentTenant() + "/controller/v1/4712/softwaremodules/"
|
||||||
+ "/controller/v1/4712/softwaremodules/"
|
|
||||||
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
|
+ findDistributionSetByAction.findFirstModuleByType(osType).get().getId()
|
||||||
+ "/artifacts/test1.signature")))
|
+ "/artifacts/test1.signature")))
|
||||||
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
|
.andExpect(jsonPath("$.deployment.chunks[?(@.part==os)].artifacts[1]._links.md5sum-http.href",
|
||||||
@@ -268,11 +268,12 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
final String visibleMetadataOsKey = "metaDataVisible";
|
final String visibleMetadataOsKey = "metaDataVisible";
|
||||||
final String visibleMetadataOsValue = "withValue";
|
final String visibleMetadataOsValue = "withValue";
|
||||||
|
|
||||||
final byte random[] = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
||||||
false);
|
false, artifactSize);
|
||||||
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
||||||
"test1.signature", false);
|
"test1.signature", false, artifactSize);
|
||||||
|
|
||||||
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
|
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(getOsModule(ds))
|
||||||
.key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true));
|
.key(visibleMetadataOsKey).value(visibleMetadataOsValue).targetVisible(true));
|
||||||
@@ -391,11 +392,12 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest {
|
|||||||
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
final DistributionSet ds = testdataFactory.createDistributionSet("", true);
|
||||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("2", true);
|
||||||
|
|
||||||
final byte random[] = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomUtils.nextBytes(artifactSize);
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds), "test1",
|
||||||
false);
|
false, artifactSize);
|
||||||
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
final Artifact artifactSignature = artifactManagement.create(new ByteArrayInputStream(random), getOsModule(ds),
|
||||||
"test1.signature", false);
|
"test1.signature", false, artifactSize);
|
||||||
|
|
||||||
final Target savedTarget = testdataFactory.createTarget("4712");
|
final Target savedTarget = testdataFactory.createTarget("4712");
|
||||||
|
|
||||||
|
|||||||
@@ -46,11 +46,13 @@ final class MgmtRestModelMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a action rest type to a action repository type.
|
* Convert the given {@link MgmtActionType} into a corresponding repository
|
||||||
|
* {@link ActionType}.
|
||||||
*
|
*
|
||||||
* @param actionTypeRest
|
* @param actionTypeRest
|
||||||
* the rest type
|
* the REST representation of the action type
|
||||||
* @return <null> or the action repository type
|
*
|
||||||
|
* @return <null> or the repository action type
|
||||||
*/
|
*/
|
||||||
public static ActionType convertActionType(final MgmtActionType actionTypeRest) {
|
public static ActionType convertActionType(final MgmtActionType actionTypeRest) {
|
||||||
if (actionTypeRest == null) {
|
if (actionTypeRest == null) {
|
||||||
@@ -70,11 +72,13 @@ final class MgmtRestModelMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a action repository type to rest type.
|
* Converts the given repository {@link ActionType} into a corresponding
|
||||||
|
* {@link MgmtActionType}.
|
||||||
*
|
*
|
||||||
* @param actionType
|
* @param actionType
|
||||||
* the rest type
|
* the repository representation of the action type
|
||||||
* @return <null> or the action repository type
|
*
|
||||||
|
* @return <null> or the REST action type
|
||||||
*/
|
*/
|
||||||
public static MgmtActionType convertActionType(final ActionType actionType) {
|
public static MgmtActionType convertActionType(final ActionType actionType) {
|
||||||
if (actionType == null) {
|
if (actionType == null) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class MgmtSoftwareModuleResource implements MgmtSoftwareModuleRestApi {
|
|||||||
try (InputStream in = file.getInputStream()) {
|
try (InputStream in = file.getInputStream()) {
|
||||||
final Artifact result = artifactManagement.create(in, softwareModuleId, fileName,
|
final Artifact result = artifactManagement.create(in, softwareModuleId, fileName,
|
||||||
md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(), false,
|
md5Sum == null ? null : md5Sum.toLowerCase(), sha1Sum == null ? null : sha1Sum.toLowerCase(), false,
|
||||||
file.getContentType());
|
file.getContentType(), file.getSize());
|
||||||
|
|
||||||
final MgmtArtifact reponse = MgmtSoftwareModuleMapper.toResponse(result);
|
final MgmtArtifact reponse = MgmtSoftwareModuleMapper.toResponse(result);
|
||||||
MgmtSoftwareModuleMapper.addLinks(result, reponse);
|
MgmtSoftwareModuleMapper.addLinks(result, reponse);
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||||
@@ -44,9 +47,11 @@ import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.jayway.jsonpath.JsonPath;
|
import com.jayway.jsonpath.JsonPath;
|
||||||
|
|
||||||
@@ -179,6 +184,45 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet.getId() + "/assignedSM"))
|
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet.getId() + "/assignedSM"))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
.andExpect(jsonPath("$.size", equalTo(smIDs.size())));
|
.andExpect(jsonPath("$.size", equalTo(smIDs.size())));
|
||||||
|
|
||||||
|
// verify quota enforcement
|
||||||
|
final int maxSoftwareModules = quotaManagement.getMaxSoftwareModulesPerDistributionSet();
|
||||||
|
final List<Long> moduleIDs = Lists.newArrayList();
|
||||||
|
for (int i = 0; i < maxSoftwareModules + 1; ++i) {
|
||||||
|
moduleIDs.add(testdataFactory.createSoftwareModuleApp("sm" + i).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// post assignment
|
||||||
|
final String jsonIDs = JsonBuilder.ids(moduleIDs.subList(0, maxSoftwareModules - smIDs.size()));
|
||||||
|
mvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet.getId() + "/assignedSM")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(jsonIDs)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
// test if size corresponds with quota
|
||||||
|
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet.getId()
|
||||||
|
+ "/assignedSM?limit={limit}", maxSoftwareModules * 2)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(maxSoftwareModules)));
|
||||||
|
|
||||||
|
// post one more to cause the quota to be exceeded
|
||||||
|
mvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet.getId() + "/assignedSM")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(JsonBuilder.ids(Collections.singletonList(moduleIDs.get(moduleIDs.size() - 1)))))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
// verify quota is also enforced for bulk uploads
|
||||||
|
final DistributionSet disSet2 = testdataFactory.createDistributionSetWithNoSoftwareModules("Saturn", "4.0");
|
||||||
|
mvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet2.getId() + "/assignedSM")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(JsonBuilder.ids(moduleIDs)))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
// verify size is still 0
|
||||||
|
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + disSet2.getId() + "/assignedSM"))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
|
.andExpect(jsonPath("$.size", equalTo(0)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -229,6 +273,48 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
.as("Five targets in repository have DS assigned").hasSize(5);
|
.as("Five targets in repository have DS assigned").hasSize(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that multi target assignment is protected by our 'max targets per manual assignment' quota.")
|
||||||
|
public void assignMultipleTargetsToDistributionSetUntilQuotaIsExceeded() throws Exception {
|
||||||
|
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerManualAssignment();
|
||||||
|
final List<Target> targets = testdataFactory.createTargets(maxTargets + 1);
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
final JSONArray payload = new JSONArray();
|
||||||
|
targets.forEach(trg -> payload.put(new JSONObject().put("id", trg.getId())));
|
||||||
|
|
||||||
|
mvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + ds.getId() + "/assignedTargets")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(payload.toString())).andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds.getId()).getContent()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that the 'max actions per target' quota is enforced if the distribution set assignment of a target is changed permanently")
|
||||||
|
public void changeDistributionSetAssignmentForTargetUntilQuotaIsExceeded() throws Exception {
|
||||||
|
|
||||||
|
// create one target
|
||||||
|
final Target testTarget = testdataFactory.createTarget("trg1");
|
||||||
|
final int maxActions = quotaManagement.getMaxActionsPerTarget();
|
||||||
|
|
||||||
|
// create a set of distribution sets
|
||||||
|
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||||
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||||
|
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
|
||||||
|
|
||||||
|
IntStream.range(0, maxActions).forEach(i -> {
|
||||||
|
// toggle the distribution set
|
||||||
|
assignDistributionSet(i % 2 == 0 ? ds1 : ds2, testTarget);
|
||||||
|
});
|
||||||
|
|
||||||
|
// assign our test target to another distribution set and verify that
|
||||||
|
// the 'max actions per target' quota is exceeded
|
||||||
|
final String json = new JSONArray().put(new JSONObject().put("id", testTarget.getControllerId())).toString();
|
||||||
|
mvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + ds3.getId() + "/assignedTargets")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(json)).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that offline reported multi target assignment through API is reflected by the repository.")
|
@Description("Ensures that offline reported multi target assignment through API is reflected by the repository.")
|
||||||
public void offlineAssignmentOfMultipleTargetsToDistributionSet() throws Exception {
|
public void offlineAssignmentOfMultipleTargetsToDistributionSet() throws Exception {
|
||||||
@@ -433,14 +519,13 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
final Set<DistributionSet> createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1);
|
final Set<DistributionSet> createDistributionSetsAlphabetical = createDistributionSetsAlphabetical(1);
|
||||||
final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next();
|
final DistributionSet createdDs = createDistributionSetsAlphabetical.iterator().next();
|
||||||
|
|
||||||
targetFilterQueryManagement.updateAutoAssignDS(
|
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
|
||||||
targetFilterQueryManagement
|
.create(entityFactory.targetFilterQuery().create().name(knownFilterName).query("name==y")).getId(),
|
||||||
.create(entityFactory.targetFilterQuery().create().name(knownFilterName).query("x==y")).getId(),
|
|
||||||
createdDs.getId());
|
createdDs.getId());
|
||||||
|
|
||||||
// create some dummy target filter queries
|
// create some dummy target filter queries
|
||||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("b").query("x==y"));
|
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("b").query("name==y"));
|
||||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("c").query("x==y"));
|
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("c").query("name==y"));
|
||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId()
|
mvc.perform(get(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/" + createdDs.getId()
|
||||||
+ "/autoAssignTargetFilters")).andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(1)))
|
+ "/autoAssignTargetFilters")).andExpect(status().isOk()).andExpect(jsonPath("$.size", equalTo(1)))
|
||||||
@@ -495,16 +580,16 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
private void prepareTestFilters(final String filterNamePrefix, final DistributionSet createdDs) {
|
private void prepareTestFilters(final String filterNamePrefix, final DistributionSet createdDs) {
|
||||||
// create target filter queries that should be found
|
// create target filter queries that should be found
|
||||||
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
|
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
|
||||||
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "1").query("x==y")).getId(),
|
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "1").query("name==y"))
|
||||||
createdDs.getId());
|
.getId(), createdDs.getId());
|
||||||
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
|
targetFilterQueryManagement.updateAutoAssignDS(targetFilterQueryManagement
|
||||||
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "2").query("x==y")).getId(),
|
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "2").query("name==y"))
|
||||||
createdDs.getId());
|
.getId(), createdDs.getId());
|
||||||
// create some dummy target filter queries
|
// create some dummy target filter queries
|
||||||
targetFilterQueryManagement
|
targetFilterQueryManagement
|
||||||
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "b").query("x==y"));
|
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "b").query("name==y"));
|
||||||
targetFilterQueryManagement
|
targetFilterQueryManagement
|
||||||
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "c").query("x==y"));
|
.create(entityFactory.targetFilterQuery().create().name(filterNamePrefix + "c").query("name==y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -902,12 +987,12 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
final String knownValue1 = "knownValue1";
|
final String knownValue1 = "knownValue1";
|
||||||
final String knownValue2 = "knownValue2";
|
final String knownValue2 = "knownValue2";
|
||||||
|
|
||||||
final JSONArray jsonArray = new JSONArray();
|
final JSONArray metaData1 = new JSONArray();
|
||||||
jsonArray.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
|
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
|
||||||
jsonArray.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
|
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2));
|
||||||
|
|
||||||
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId()).accept(MediaType.APPLICATION_JSON)
|
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId()).accept(MediaType.APPLICATION_JSON)
|
||||||
.contentType(MediaType.APPLICATION_JSON).content(jsonArray.toString()))
|
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||||
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
|
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
|
||||||
@@ -921,6 +1006,25 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
|||||||
|
|
||||||
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
|
assertThat(metaKey1.getValue()).isEqualTo(knownValue1);
|
||||||
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
|
assertThat(metaKey2.getValue()).isEqualTo(knownValue2);
|
||||||
|
|
||||||
|
// verify quota enforcement
|
||||||
|
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
|
||||||
|
|
||||||
|
final JSONArray metaData2 = new JSONArray();
|
||||||
|
for (int i = 0; i < maxMetaData - metaData1.length() + 1; ++i) {
|
||||||
|
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/distributionsets/{dsId}/metadata", testDS.getId()).accept(MediaType.APPLICATION_JSON)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
// verify that the number of meta data entries has not changed
|
||||||
|
// (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||||
|
assertThat(distributionSetManagement
|
||||||
|
.findMetaDataByDistributionSetId(new PageRequest(0, Integer.MAX_VALUE), testDS.getId())
|
||||||
|
.getTotalElements()).isEqualTo(metaData1.length());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
@@ -239,6 +243,60 @@ public class MgmtDistributionSetTypeResourceTest extends AbstractManagementApiIn
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@WithUser(principal = "uploadTester", allSpPermissions = true)
|
||||||
|
@Description("Verifies quota enforcement for /rest/v1/distributionsettypes/{ID}/optionalmoduletypes POST requests.")
|
||||||
|
public void assignModuleTypesToDistributionSetTypeUntilQuotaExceeded() throws Exception {
|
||||||
|
|
||||||
|
// create software module types
|
||||||
|
final int maxSoftwareModuleTypes = quotaManagement.getMaxSoftwareModuleTypesPerDistributionSetType();
|
||||||
|
final List<Long> moduleTypeIds = Lists.newArrayList();
|
||||||
|
for (int i = 0; i < maxSoftwareModuleTypes + 1; ++i) {
|
||||||
|
final SoftwareModuleTypeCreate smCreate = entityFactory.softwareModuleType().create().name("smType_" + i)
|
||||||
|
.description("smType_" + i).maxAssignments(1).colour("blue").key("smType_" + i);
|
||||||
|
moduleTypeIds.add(softwareModuleTypeManagement.create(smCreate).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify quota enforcement for optional module types
|
||||||
|
|
||||||
|
final DistributionSetType testType = distributionSetTypeManagement.create(entityFactory.distributionSetType()
|
||||||
|
.create().key("testType").name("testType").description("testType").colour("col12"));
|
||||||
|
assertThat(testType.getOptLockRevision()).isEqualTo(1);
|
||||||
|
|
||||||
|
for (int i = 0; i < moduleTypeIds.size() - 1; ++i) {
|
||||||
|
mvc.perform(post("/rest/v1/distributionsettypes/{dstID}/optionalmoduletypes", testType.getId())
|
||||||
|
.content("{\"id\":" + moduleTypeIds.get(i) + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/distributionsettypes/{dstID}/optionalmoduletypes", testType.getId())
|
||||||
|
.content("{\"id\":" + moduleTypeIds.get(moduleTypeIds.size() - 1) + "}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
// verify quota enforcement for mandatory module types
|
||||||
|
|
||||||
|
final DistributionSetType testType2 = distributionSetTypeManagement.create(entityFactory.distributionSetType()
|
||||||
|
.create().key("testType2").name("testType2").description("testType2").colour("col12"));
|
||||||
|
assertThat(testType2.getOptLockRevision()).isEqualTo(1);
|
||||||
|
|
||||||
|
for (int i = 0; i < moduleTypeIds.size() - 1; ++i) {
|
||||||
|
mvc.perform(post("/rest/v1/distributionsettypes/{dstID}/mandatorymoduletypes", testType2.getId())
|
||||||
|
.content("{\"id\":" + moduleTypeIds.get(i) + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/distributionsettypes/{dstID}/mandatorymoduletypes", testType2.getId())
|
||||||
|
.content("{\"id\":" + moduleTypeIds.get(moduleTypeIds.size() - 1) + "}")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithUser(principal = "uploadTester", allSpPermissions = true)
|
@WithUser(principal = "uploadTester", allSpPermissions = true)
|
||||||
@Description("Checks the correct behaviour of /rest/v1/distributionsettypes/{ID}/mandatorymoduletypes GET requests.")
|
@Description("Checks the correct behaviour of /rest/v1/distributionsettypes/{ID}/mandatorymoduletypes GET requests.")
|
||||||
|
|||||||
@@ -27,9 +27,11 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
@@ -131,9 +133,45 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
postRollout("rollout1", 10, dsA.getId(), "id==target*", 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that rollout cannot be created if too many rollout groups are specified.")
|
||||||
|
public void createRolloutWithTooManyRolloutGroups() throws Exception {
|
||||||
|
|
||||||
|
final int maxGroups = quotaManagement.getMaxRolloutGroupsPerRollout();
|
||||||
|
testdataFactory.createTargets(20, "target", "rollout");
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/rollouts")
|
||||||
|
.content(JsonBuilder.rollout("rollout1", "rollout1Desc", maxGroups + 1,
|
||||||
|
testdataFactory.createDistributionSet("ds").getId(), "id==target*",
|
||||||
|
new RolloutGroupConditionBuilder().withDefaults().build()))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that rollout cannot be created if the 'max targets per rollout group' quota would be violated for one of the groups.")
|
||||||
|
public void createRolloutFailsIfRolloutGroupQuotaIsViolated() throws Exception {
|
||||||
|
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target", "rollout");
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/rollouts")
|
||||||
|
.content(JsonBuilder.rollout("rollout1", "rollout1Desc", 1,
|
||||||
|
testdataFactory.createDistributionSet("ds").getId(), "id==target*",
|
||||||
|
new RolloutGroupConditionBuilder().withDefaults().build()))
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Testing that rollout can be created with groups")
|
@Description("Testing that rollout can be created with groups")
|
||||||
public void createRolloutWithGroupsDefinitions() throws Exception {
|
public void createRolloutWithGroupDefinitions() throws Exception {
|
||||||
final DistributionSet dsA = testdataFactory.createDistributionSet("ro");
|
final DistributionSet dsA = testdataFactory.createDistributionSet("ro");
|
||||||
|
|
||||||
final int amountTargets = 10;
|
final int amountTargets = 10;
|
||||||
@@ -160,7 +198,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Testing that no rollout with groups that have illegal percentages can be created")
|
@Description("Testing that no rollout with groups that have illegal percentages can be created")
|
||||||
public void createRolloutWithToLowlPercentage() throws Exception {
|
public void createRolloutWithTooLowPercentage() throws Exception {
|
||||||
final DistributionSet dsA = testdataFactory.createDistributionSet("ro2");
|
final DistributionSet dsA = testdataFactory.createDistributionSet("ro2");
|
||||||
|
|
||||||
final int amountTargets = 10;
|
final int amountTargets = 10;
|
||||||
@@ -185,7 +223,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Testing that no rollout with groups that have illegal percentages can be created")
|
@Description("Testing that no rollout with groups that have illegal percentages can be created")
|
||||||
public void createRolloutWithToHighPercentage() throws Exception {
|
public void createRolloutWithTooHighPercentage() throws Exception {
|
||||||
final DistributionSet dsA = testdataFactory.createDistributionSet("ro2");
|
final DistributionSet dsA = testdataFactory.createDistributionSet("ro2");
|
||||||
|
|
||||||
final int amountTargets = 10;
|
final int amountTargets = 10;
|
||||||
@@ -217,7 +255,7 @@ public class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Terives sinle rollout from management API includinfg extra data that is delieverd only for single rollout access.")
|
@Description("Retrieves single rollout from management API including extra data that is delivered only for single rollout access.")
|
||||||
public void retrieveSingleRollout() throws Exception {
|
public void retrieveSingleRollout() throws Exception {
|
||||||
testdataFactory.createTargets(20, "rollout", "rollout");
|
testdataFactory.createTargets(20, "rollout", "rollout");
|
||||||
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
final DistributionSet dsA = testdataFactory.createDistributionSet("");
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
@@ -36,6 +37,7 @@ import org.eclipse.hawkbit.exception.SpServerError;
|
|||||||
import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact;
|
import org.eclipse.hawkbit.mgmt.json.model.artifact.MgmtArtifact;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
import org.eclipse.hawkbit.repository.Constants;
|
import org.eclipse.hawkbit.repository.Constants;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
@@ -49,8 +51,10 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.mock.web.MockMultipartFile;
|
import org.springframework.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.web.servlet.MvcResult;
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
@@ -66,6 +70,7 @@ import ru.yandex.qatools.allure.annotations.Stories;
|
|||||||
*/
|
*/
|
||||||
@Features("Component Tests - Management API")
|
@Features("Component Tests - Management API")
|
||||||
@Stories("Software Module Resource")
|
@Stories("Software Module Resource")
|
||||||
|
@TestPropertySource(properties = { "hawkbit.server.security.dos.maxArtifactSize=100000" })
|
||||||
public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTest {
|
public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegrationTest {
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -147,7 +152,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the uppload of an artifact binary. The upload is executed and the content checked in the repository for completenes.")
|
@Description("Tests the upload of an artifact binary. The upload is executed and the content checked in the repository for completeness.")
|
||||||
public void uploadArtifact() throws Exception {
|
public void uploadArtifact() throws Exception {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
@@ -183,6 +188,24 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
assertArtifact(sm, random);
|
assertArtifact(sm, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that artifacts which exceed the configured maximum size cannot be uploaded.")
|
||||||
|
public void uploadArtifactFailsIfTooLarge() throws Exception {
|
||||||
|
final SoftwareModule sm = testdataFactory.createSoftwareModule("quota", "quota");
|
||||||
|
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||||
|
|
||||||
|
// create a file which exceeds the configured maximum size
|
||||||
|
final byte[] randomBytes = new byte[Math.toIntExact(maxSize) + 1024];
|
||||||
|
new Random().nextBytes(randomBytes);
|
||||||
|
|
||||||
|
final MockMultipartFile file = new MockMultipartFile("file", "origFilename", null, randomBytes);
|
||||||
|
|
||||||
|
// try to upload
|
||||||
|
mvc.perform(fileUpload("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
private void assertArtifact(final SoftwareModule sm, final byte[] random) throws IOException {
|
private void assertArtifact(final SoftwareModule sm, final byte[] random) throws IOException {
|
||||||
// check result in db...
|
// check result in db...
|
||||||
// repo
|
// repo
|
||||||
@@ -312,17 +335,57 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that only a limited number of artifacts can be uploaded for one software module.")
|
||||||
|
public void uploadArtifactsUntilQuotaExceeded() throws Exception {
|
||||||
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
||||||
|
|
||||||
|
for (int i = 0; i < maxArtifacts; ++i) {
|
||||||
|
// create test file
|
||||||
|
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
||||||
|
final String md5sum = HashGeneratorUtils.generateMD5(random);
|
||||||
|
final String sha1sum = HashGeneratorUtils.generateSHA1(random);
|
||||||
|
final MockMultipartFile file = new MockMultipartFile("file", "origFilename" + i, null, random);
|
||||||
|
|
||||||
|
// upload
|
||||||
|
mvc.perform(fileUpload("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isCreated())
|
||||||
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||||
|
.andExpect(jsonPath("$.hashes.md5", equalTo(md5sum)))
|
||||||
|
.andExpect(jsonPath("$.hashes.sha1", equalTo(sha1sum)))
|
||||||
|
.andExpect(jsonPath("$.size", equalTo(random.length)))
|
||||||
|
.andExpect(jsonPath("$.providedFilename", equalTo("origFilename" + i))).andReturn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// upload one more file to cause the quota to be exceeded
|
||||||
|
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
||||||
|
HashGeneratorUtils.generateMD5(random);
|
||||||
|
HashGeneratorUtils.generateSHA1(random);
|
||||||
|
final MockMultipartFile file = new MockMultipartFile("file", "origFilename_final", null, random);
|
||||||
|
|
||||||
|
// upload
|
||||||
|
mvc.perform(fileUpload("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).file(file)
|
||||||
|
.accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print())
|
||||||
|
.andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath("$.exceptionClass", equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath("$.errorCode", equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests binary download of an artifact including verfication that the downloaded binary is consistent and that the etag header is as expected identical to the SHA1 hash of the file.")
|
@Description("Tests binary download of an artifact including verfication that the downloaded binary is consistent and that the etag header is as expected identical to the SHA1 hash of the file.")
|
||||||
public void downloadArtifact() throws Exception {
|
public void downloadArtifact() throws Exception {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
||||||
false);
|
false, artifactSize);
|
||||||
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2",
|
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2",
|
||||||
false);
|
false, artifactSize);
|
||||||
|
|
||||||
downloadAndVerify(sm, random, artifact);
|
downloadAndVerify(sm, random, artifact);
|
||||||
downloadAndVerify(sm, random, artifact2);
|
downloadAndVerify(sm, random, artifact2);
|
||||||
@@ -348,9 +411,11 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
// prepare data for test
|
// prepare data for test
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
||||||
false);
|
false, artifactSize);
|
||||||
|
|
||||||
// perform test
|
// perform test
|
||||||
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artId}", sm.getId(), artifact.getId())
|
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts/{artId}", sm.getId(), artifact.getId())
|
||||||
@@ -373,12 +438,13 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
public void getArtifacts() throws Exception {
|
public void getArtifacts() throws Exception {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
||||||
false);
|
false, artifactSize);
|
||||||
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2",
|
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2",
|
||||||
false);
|
false, artifactSize);
|
||||||
|
|
||||||
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).accept(MediaType.APPLICATION_JSON))
|
mvc.perform(get("/rest/v1/softwaremodules/{smId}/artifacts", sm.getId()).accept(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
@@ -402,7 +468,9 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
@Test
|
@Test
|
||||||
@Description("Verfies that the system refuses unsupported request types and answers as defined to them, e.g. NOT FOUND on a non existing resource. Or a HTTP POST for updating a resource results in METHOD NOT ALLOWED etc.")
|
@Description("Verfies that the system refuses unsupported request types and answers as defined to them, e.g. NOT FOUND on a non existing resource. Or a HTTP POST for updating a resource results in METHOD NOT ALLOWED etc.")
|
||||||
public void invalidRequestsOnArtifactResource() throws Exception {
|
public void invalidRequestsOnArtifactResource() throws Exception {
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
|
||||||
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
final MockMultipartFile file = new MockMultipartFile("file", "orig", null, random);
|
final MockMultipartFile file = new MockMultipartFile("file", "orig", null, random);
|
||||||
|
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
@@ -415,7 +483,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isNotFound());
|
||||||
|
|
||||||
// SM does not exist
|
// SM does not exist
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize);
|
||||||
mvc.perform(get("/rest/v1/softwaremodules/1234567890/artifacts")).andDo(MockMvcResultPrinter.print())
|
mvc.perform(get("/rest/v1/softwaremodules/1234567890/artifacts")).andDo(MockMvcResultPrinter.print())
|
||||||
.andExpect(status().isNotFound());
|
.andExpect(status().isNotFound());
|
||||||
|
|
||||||
@@ -723,9 +791,10 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
|
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false, artifactSize);
|
||||||
|
|
||||||
assertThat(softwareModuleManagement.findAll(PAGE)).as("Softwaremoudle size is wrong").hasSize(1);
|
assertThat(softwareModuleManagement.findAll(PAGE)).as("Softwaremoudle size is wrong").hasSize(1);
|
||||||
assertThat(artifactManagement.count()).isEqualTo(1);
|
assertThat(artifactManagement.count()).isEqualTo(1);
|
||||||
@@ -743,11 +812,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
public void deleteAssignedSoftwareModule() throws Exception {
|
public void deleteAssignedSoftwareModule() throws Exception {
|
||||||
final DistributionSet ds1 = testdataFactory.createDistributionSet("a");
|
final DistributionSet ds1 = testdataFactory.createDistributionSet("a");
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
final Long appTypeSmId = ds1.findFirstModuleByType(appType).get().getId();
|
final Long appTypeSmId = ds1.findFirstModuleByType(appType).get().getId();
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), appTypeSmId, "file1", false);
|
artifactManagement.create(new ByteArrayInputStream(random), appTypeSmId, "file1", false, artifactSize);
|
||||||
|
|
||||||
assertThat(softwareModuleManagement.count()).isEqualTo(3);
|
assertThat(softwareModuleManagement.count()).isEqualTo(3);
|
||||||
assertThat(artifactManagement.count()).isEqualTo(1);
|
assertThat(artifactManagement.count()).isEqualTo(1);
|
||||||
@@ -771,12 +841,13 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
// Create 1 SM
|
// Create 1 SM
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
// Create 2 artifacts
|
// Create 2 artifacts
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1",
|
||||||
false);
|
false, artifactSize);
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2", false);
|
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file2", false, artifactSize);
|
||||||
|
|
||||||
// check repo before delete
|
// check repo before delete
|
||||||
assertThat(softwareModuleManagement.findAll(PAGE)).hasSize(1);
|
assertThat(softwareModuleManagement.findAll(PAGE)).hasSize(1);
|
||||||
@@ -797,7 +868,7 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verfies the successfull creation of metadata.")
|
@Description("Verfies the successful creation of metadata and the enforcement of the meta data quota.")
|
||||||
public void createMetadata() throws Exception {
|
public void createMetadata() throws Exception {
|
||||||
|
|
||||||
final String knownKey1 = "knownKey1";
|
final String knownKey1 = "knownKey1";
|
||||||
@@ -807,12 +878,12 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
|
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
final JSONArray jsonArray = new JSONArray();
|
final JSONArray metaData1 = new JSONArray();
|
||||||
jsonArray.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
|
metaData1.put(new JSONObject().put("key", knownKey1).put("value", knownValue1));
|
||||||
jsonArray.put(new JSONObject().put("key", knownKey2).put("value", knownValue2).put("targetVisible", true));
|
metaData1.put(new JSONObject().put("key", knownKey2).put("value", knownValue2).put("targetVisible", true));
|
||||||
|
|
||||||
mvc.perform(post("/rest/v1/softwaremodules/{swId}/metadata", sm.getId()).accept(MediaType.APPLICATION_JSON)
|
mvc.perform(post("/rest/v1/softwaremodules/{swId}/metadata", sm.getId()).accept(MediaType.APPLICATION_JSON)
|
||||||
.contentType(MediaType.APPLICATION_JSON).content(jsonArray.toString()))
|
.contentType(MediaType.APPLICATION_JSON).content(metaData1.toString()))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isCreated())
|
||||||
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
|
||||||
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
|
.andExpect(jsonPath("[0]key", equalTo(knownKey1))).andExpect(jsonPath("[0]value", equalTo(knownValue1)))
|
||||||
@@ -827,6 +898,25 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
|
|
||||||
assertThat(metaKey1.getValue()).as("Metadata key is wrong").isEqualTo(knownValue1);
|
assertThat(metaKey1.getValue()).as("Metadata key is wrong").isEqualTo(knownValue1);
|
||||||
assertThat(metaKey2.getValue()).as("Metadata key is wrong").isEqualTo(knownValue2);
|
assertThat(metaKey2.getValue()).as("Metadata key is wrong").isEqualTo(knownValue2);
|
||||||
|
|
||||||
|
// verify quota enforcement
|
||||||
|
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||||
|
|
||||||
|
final JSONArray metaData2 = new JSONArray();
|
||||||
|
for (int i = 0; i < maxMetaData - metaData1.length() + 1; ++i) {
|
||||||
|
metaData2.put(new JSONObject().put("key", knownKey1 + i).put("value", knownValue1 + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
mvc.perform(post("/rest/v1/softwaremodules/{swId}/metadata", sm.getId()).accept(MediaType.APPLICATION_JSON)
|
||||||
|
.contentType(MediaType.APPLICATION_JSON).content(metaData2.toString()))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden());
|
||||||
|
|
||||||
|
// verify that the number of meta data entries has not changed
|
||||||
|
// (we cannot use the PAGE constant here as it tries to sort by ID)
|
||||||
|
assertThat(softwareModuleManagement
|
||||||
|
.findMetaDataBySoftwareModuleId(new PageRequest(0, Integer.MAX_VALUE), sm.getId()).getTotalElements())
|
||||||
|
.isEqualTo(metaData1.length());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -930,4 +1020,5 @@ public class MgmtSoftwareModuleResourceTest extends AbstractManagementApiIntegra
|
|||||||
character++;
|
character++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.exception.SpServerError;
|
import org.eclipse.hawkbit.exception.SpServerError;
|
||||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
import org.eclipse.hawkbit.rest.exception.MessageNotReadableException;
|
||||||
@@ -53,6 +54,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
private static final String JSON_PATH_FIELD_SIZE = ".size";
|
||||||
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
private static final String JSON_PATH_FIELD_TOTAL = ".total";
|
||||||
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet";
|
private static final String JSON_PATH_FIELD_AUTO_ASSIGN_DS = ".autoAssignDistributionSet";
|
||||||
|
private static final String JSON_PATH_FIELD_EXCEPTION_CLASS = ".exceptionClass";
|
||||||
|
private static final String JSON_PATH_FIELD_ERROR_CODE = ".errorCode";
|
||||||
|
|
||||||
// target
|
// target
|
||||||
// $.field
|
// $.field
|
||||||
@@ -64,6 +67,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
|
private static final String JSON_PATH_ID = JSON_PATH_ROOT + JSON_PATH_FIELD_ID;
|
||||||
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
|
private static final String JSON_PATH_QUERY = JSON_PATH_ROOT + JSON_PATH_FIELD_QUERY;
|
||||||
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
|
private static final String JSON_PATH_AUTO_ASSIGN_DS = JSON_PATH_ROOT + JSON_PATH_FIELD_AUTO_ASSIGN_DS;
|
||||||
|
private static final String JSON_PATH_EXCEPTION_CLASS = JSON_PATH_ROOT + JSON_PATH_FIELD_EXCEPTION_CLASS;
|
||||||
|
private static final String JSON_PATH_ERROR_CODE = JSON_PATH_ROOT + JSON_PATH_FIELD_ERROR_CODE;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that deletion is executed if permitted.")
|
@Description("Ensures that deletion is executed if permitted.")
|
||||||
@@ -126,8 +131,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
final String body = new JSONObject().put("name", filterName2).toString();
|
final String body = new JSONObject().put("name", filterName2).toString();
|
||||||
|
|
||||||
// prepare
|
// prepare
|
||||||
final TargetFilterQuery tfq = targetFilterQueryManagement.create(
|
final TargetFilterQuery tfq = targetFilterQueryManagement
|
||||||
entityFactory.targetFilterQuery().create().name(filterName).query(filterQuery));
|
.create(entityFactory.targetFilterQuery().create().name(filterName).query(filterQuery));
|
||||||
|
|
||||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId()).content(body)
|
||||||
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
.contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||||
@@ -230,6 +235,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Description("Ensures that a single target filter query can be retrieved via its id.")
|
||||||
public void getSingleTarget() throws Exception {
|
public void getSingleTarget() throws Exception {
|
||||||
// create first a target which can be retrieved by rest interface
|
// create first a target which can be retrieved by rest interface
|
||||||
final String knownQuery = "name=test01";
|
final String knownQuery = "name=test01";
|
||||||
@@ -247,6 +253,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Description("Ensures that the retrieval of a non-existing target filter query results in a HTTP Not found error (404).")
|
||||||
public void getSingleTargetNoExistsResponseNotFound() throws Exception {
|
public void getSingleTargetNoExistsResponseNotFound() throws Exception {
|
||||||
final String targetIdNotExists = "546546";
|
final String targetIdNotExists = "546546";
|
||||||
// test
|
// test
|
||||||
@@ -262,6 +269,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Description("Ensures that the creation of a target filter query based on an invalid request payload results in a HTTP Bad Request error (400).")
|
||||||
public void createTargetFilterQueryWithBadPayloadBadRequest() throws Exception {
|
public void createTargetFilterQueryWithBadPayloadBadRequest() throws Exception {
|
||||||
final String notJson = "abc";
|
final String notJson = "abc";
|
||||||
|
|
||||||
@@ -279,22 +287,72 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
|
assertThat(exceptionInfo.getErrorCode()).isEqualTo(SpServerError.SP_REST_BODY_NOT_READABLE.getKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that the assignment of an auto-assign distribution set results in a HTTP Forbidden error (403) "
|
||||||
|
+ "if the (existing) query addresses too many targets.")
|
||||||
|
public void setAutoAssignDistributionSetOnFilterQueryThatExceedsQuota() throws Exception {
|
||||||
|
|
||||||
|
// create targets
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target%s");
|
||||||
|
|
||||||
|
// create the filter query and the distribution set
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
|
final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("1", "controllerId==target*");
|
||||||
|
|
||||||
|
mvc.perform(
|
||||||
|
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||||
|
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Ensures that the update of a target filter query results in a HTTP Forbidden error (403) "
|
||||||
|
+ "if the updated query addresses too many targets.")
|
||||||
|
public void updateTargetFilterQueryWithQueryThatExceedsQuota() throws Exception {
|
||||||
|
|
||||||
|
// create targets
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target%s");
|
||||||
|
|
||||||
|
// create the filter query and the distribution set
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
|
final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("1", "controllerId==target1");
|
||||||
|
|
||||||
|
// assign the auto-assign distribution set, this should work
|
||||||
|
mvc.perform(
|
||||||
|
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||||
|
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||||
|
|
||||||
|
assertThat(targetFilterQueryManagement.get(filterQuery.getId()).get().getAutoAssignDistributionSet())
|
||||||
|
.isEqualTo(set);
|
||||||
|
|
||||||
|
// update the query of the filter query to trigger a quota hit
|
||||||
|
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
|
||||||
|
.content("{\"query\":\"controllerId==target*\"}").contentType(MediaType.APPLICATION_JSON))
|
||||||
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isForbidden())
|
||||||
|
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(QuotaExceededException.class.getName())))
|
||||||
|
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
|
public void setAutoAssignDistributionSetToTargetFilterQuery() throws Exception {
|
||||||
|
|
||||||
final String knownQuery = "name=test05";
|
final String knownQuery = "name==test05";
|
||||||
final String knownName = "filter05";
|
final String knownName = "filter05";
|
||||||
|
|
||||||
final DistributionSet set = testdataFactory.createDistributionSet("one");
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
||||||
|
|
||||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||||
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk());
|
||||||
|
|
||||||
assertThat(
|
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isEqualTo(set);
|
||||||
targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet())
|
|
||||||
.isEqualTo(set);
|
|
||||||
|
|
||||||
final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"
|
final String hrefPrefix = "http://localhost" + MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/"
|
||||||
+ tfq.getId();
|
+ tfq.getId();
|
||||||
@@ -311,7 +369,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
@Test
|
@Test
|
||||||
public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception {
|
public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception {
|
||||||
|
|
||||||
final String knownQuery = "name=test06";
|
final String knownQuery = "name==test06";
|
||||||
final String knownName = "filter06";
|
final String knownName = "filter06";
|
||||||
final String dsName = "testDS";
|
final String dsName = "testDS";
|
||||||
|
|
||||||
@@ -319,9 +377,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery);
|
||||||
targetFilterQueryManagement.updateAutoAssignDS(tfq.getId(), set.getId());
|
targetFilterQueryManagement.updateAutoAssignDS(tfq.getId(), set.getId());
|
||||||
|
|
||||||
assertThat(
|
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isEqualTo(set);
|
||||||
targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet())
|
|
||||||
.isEqualTo(set);
|
|
||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
||||||
.andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName)));
|
.andExpect(status().isOk()).andExpect(jsonPath(JSON_PATH_NAME, equalTo(dsName)));
|
||||||
@@ -329,9 +385,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
mvc.perform(delete(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
||||||
.andExpect(status().isNoContent());
|
.andExpect(status().isNoContent());
|
||||||
|
|
||||||
assertThat(
|
assertThat(targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet()).isNull();
|
||||||
targetFilterQueryManagement.get(tfq.getId()).get().getAutoAssignDistributionSet())
|
|
||||||
.isNull();
|
|
||||||
|
|
||||||
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
mvc.perform(get(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS"))
|
||||||
.andExpect(status().isNoContent());
|
.andExpect(status().isNoContent());
|
||||||
@@ -339,8 +393,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) {
|
private TargetFilterQuery createSingleTargetFilterQuery(final String name, final String query) {
|
||||||
return targetFilterQueryManagement
|
return targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name(name).query(query));
|
||||||
.create(entityFactory.targetFilterQuery().create().name(name).query(query));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public interface ArtifactManagement {
|
|||||||
* @param overrideExisting
|
* @param overrideExisting
|
||||||
* to <code>true</code> if the artifact binary can be overridden
|
* to <code>true</code> if the artifact binary can be overridden
|
||||||
* if it already exists
|
* if it already exists
|
||||||
|
* @param filesize
|
||||||
|
* the size of the file in bytes.
|
||||||
*
|
*
|
||||||
* @return uploaded {@link Artifact}
|
* @return uploaded {@link Artifact}
|
||||||
*
|
*
|
||||||
@@ -64,7 +66,7 @@ public interface ArtifactManagement {
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||||
Artifact create(@NotNull InputStream inputStream, long moduleId, final String filename,
|
Artifact create(@NotNull InputStream inputStream, long moduleId, final String filename,
|
||||||
final boolean overrideExisting);
|
final boolean overrideExisting, final long filesize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists artifact binary as provided by given InputStream. assign the
|
* Persists artifact binary as provided by given InputStream. assign the
|
||||||
@@ -85,6 +87,9 @@ public interface ArtifactManagement {
|
|||||||
* if it already exists
|
* if it already exists
|
||||||
* @param contentType
|
* @param contentType
|
||||||
* the contentType of the file
|
* the contentType of the file
|
||||||
|
* @param filesize
|
||||||
|
* the size of the file in bytes.
|
||||||
|
*
|
||||||
* @return uploaded {@link Artifact}
|
* @return uploaded {@link Artifact}
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -100,7 +105,7 @@ public interface ArtifactManagement {
|
|||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_REPOSITORY)
|
||||||
Artifact create(@NotNull InputStream stream, long moduleId, @NotEmpty String filename, String providedMd5Sum,
|
Artifact create(@NotNull InputStream stream, long moduleId, @NotEmpty String filename, String providedMd5Sum,
|
||||||
String providedSha1Sum, boolean overrideExisting, String contentType);
|
String providedSha1Sum, boolean overrideExisting, String contentType, long filesize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Garbage collects artifact binaries if only referenced by given
|
* Garbage collects artifact binaries if only referenced by given
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv
|
|||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
@@ -45,7 +46,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
public interface DeploymentManagement {
|
public interface DeploymentManagement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||||
*
|
*
|
||||||
* @param dsID
|
* @param dsID
|
||||||
@@ -66,13 +67,17 @@ public interface DeploymentManagement {
|
|||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either provided {@link DistributionSet} or {@link Target}s
|
* if either provided {@link DistributionSet} or {@link Target}s
|
||||||
* do not exist
|
* do not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of targets the distribution set can be
|
||||||
|
* assigned to at once is exceeded
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||||
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotNull ActionType actionType,
|
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotNull ActionType actionType,
|
||||||
long forcedTimestamp, @NotEmpty Collection<String> controllerIDs);
|
long forcedTimestamp, @NotEmpty Collection<String> controllerIDs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||||
*
|
*
|
||||||
* @param dsID
|
* @param dsID
|
||||||
@@ -88,13 +93,17 @@ public interface DeploymentManagement {
|
|||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either provided {@link DistributionSet} or {@link Target}s
|
* if either provided {@link DistributionSet} or {@link Target}s
|
||||||
* do not exist
|
* do not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of targets the distribution set can be
|
||||||
|
* assigned to at once is exceeded
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||||
DistributionSetAssignmentResult assignDistributionSet(long dsID,
|
DistributionSetAssignmentResult assignDistributionSet(long dsID,
|
||||||
@NotEmpty Collection<TargetWithActionType> targets);
|
@NotEmpty Collection<TargetWithActionType> targets);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
* Assigns the addressed {@link DistributionSet} to all {@link Target}s by
|
||||||
* their IDs with a specific {@link ActionType} and an action message.
|
* their IDs with a specific {@link ActionType} and an action message.
|
||||||
*
|
*
|
||||||
* @param dsID
|
* @param dsID
|
||||||
@@ -112,16 +121,20 @@ public interface DeploymentManagement {
|
|||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either provided {@link DistributionSet} or {@link Target}s
|
* if either provided {@link DistributionSet} or {@link Target}s
|
||||||
* do not exist
|
* do not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of targets the distribution set can be
|
||||||
|
* assigned to at once is exceeded
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||||
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Collection<TargetWithActionType> targets,
|
DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Collection<TargetWithActionType> targets,
|
||||||
String actionMessage);
|
String actionMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method registers an "offline" assignment, i.e. adds a completed action
|
* Registers an "offline" assignment, i.e. adds a completed action for the
|
||||||
* for the given {@link DistributionSet} to the given {@link Target}s.
|
* given {@link DistributionSet} to the given {@link Target}s.
|
||||||
*
|
*
|
||||||
* The handling differs to hawkBit managed updates my means that:<br/>
|
* The handling differs to hawkBit-managed updates by means that:<br/>
|
||||||
*
|
*
|
||||||
* <ol type="A">
|
* <ol type="A">
|
||||||
* <li>it ignores targets completely that are in
|
* <li>it ignores targets completely that are in
|
||||||
@@ -145,15 +158,18 @@ public interface DeploymentManagement {
|
|||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either provided {@link DistributionSet} or {@link Target}s
|
* if either provided {@link DistributionSet} or {@link Target}s
|
||||||
* do not exist
|
* do not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of targets the distribution set can be
|
||||||
|
* assigned to at once is exceeded
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||||
DistributionSetAssignmentResult offlineAssignedDistributionSet(Long dsID, Collection<String> controllerIDs);
|
DistributionSetAssignmentResult offlineAssignedDistributionSet(Long dsID, Collection<String> controllerIDs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels given {@link Action} for given {@link Target}. The method will
|
* Cancels the {@link Action} with the given ID. The method will immediately
|
||||||
* immediately add a {@link Status#CANCELED} status to the action. However,
|
* add a {@link Status#CANCELED} status to the action. However, it might be
|
||||||
* it might be possible that the controller will continue to work on the
|
* possible that the controller will continue to work on the cancellation.
|
||||||
* cancellation.
|
|
||||||
*
|
*
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* to be canceled
|
* to be canceled
|
||||||
@@ -170,7 +186,7 @@ public interface DeploymentManagement {
|
|||||||
Action cancelAction(long actionId);
|
Action cancelAction(long actionId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* counts all actions associated to a specific target.
|
* Counts all actions associated to a specific target.
|
||||||
*
|
*
|
||||||
* @param rsqlParam
|
* @param rsqlParam
|
||||||
* rsql query string
|
* rsql query string
|
||||||
@@ -202,7 +218,7 @@ public interface DeploymentManagement {
|
|||||||
long countActionsAll();
|
long countActionsAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* counts all actions associated to a specific target.
|
* Counts all actions associated to a specific target.
|
||||||
*
|
*
|
||||||
* @param controllerId
|
* @param controllerId
|
||||||
* the target associated to the actions to count
|
* the target associated to the actions to count
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
|
|||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||||
@@ -52,6 +53,7 @@ public interface DistributionSetManagement
|
|||||||
* to assign and update
|
* to assign and update
|
||||||
* @param moduleIds
|
* @param moduleIds
|
||||||
* to get assigned
|
* to get assigned
|
||||||
|
*
|
||||||
* @return the updated {@link DistributionSet}.
|
* @return the updated {@link DistributionSet}.
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -62,8 +64,12 @@ public interface DistributionSetManagement
|
|||||||
* the DS is already in use.
|
* the DS is already in use.
|
||||||
*
|
*
|
||||||
* @throws UnsupportedSoftwareModuleForThisDistributionSetException
|
* @throws UnsupportedSoftwareModuleForThisDistributionSetException
|
||||||
* is {@link SoftwareModule#getType()} is not supported by this
|
* if {@link SoftwareModule#getType()} is not supported by this
|
||||||
* {@link DistributionSet#getType()}.
|
* {@link DistributionSet#getType()}.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link SoftwareModule}s is exceeded
|
||||||
|
* for the addressed {@link DistributionSet}.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
DistributionSet assignSoftwareModules(long setId, @NotEmpty Collection<Long> moduleIds);
|
DistributionSet assignSoftwareModules(long setId, @NotEmpty Collection<Long> moduleIds);
|
||||||
@@ -76,6 +82,7 @@ public interface DistributionSetManagement
|
|||||||
* to assign for
|
* to assign for
|
||||||
* @param tagId
|
* @param tagId
|
||||||
* to assign
|
* to assign
|
||||||
|
*
|
||||||
* @return list of assigned ds
|
* @return list of assigned ds
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -86,7 +93,7 @@ public interface DistributionSetManagement
|
|||||||
List<DistributionSet> assignTag(@NotEmpty Collection<Long> setIds, long tagId);
|
List<DistributionSet> assignTag(@NotEmpty Collection<Long> setIds, long tagId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a list of distribution set meta data entries.
|
* Creates a list of distribution set meta data entries.
|
||||||
*
|
*
|
||||||
* @param setId
|
* @param setId
|
||||||
* if the {@link DistributionSet} the metadata has to be created
|
* if the {@link DistributionSet} the metadata has to be created
|
||||||
@@ -97,15 +104,20 @@ public interface DistributionSetManagement
|
|||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if given set does not exist
|
* if given set does not exist
|
||||||
|
*
|
||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* in case one of the meta data entry already exists for the
|
* in case one of the meta data entry already exists for the
|
||||||
* specific key
|
* specific key
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link MetaData} entries is exceeded
|
||||||
|
* for the addressed {@link DistributionSet}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
List<DistributionSetMetadata> createMetaData(long setId, @NotEmpty Collection<MetaData> metadata);
|
List<DistributionSetMetadata> createMetaData(long setId, @NotEmpty Collection<MetaData> metadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deletes a distribution set meta data entry.
|
* Deletes a distribution set meta data entry.
|
||||||
*
|
*
|
||||||
* @param setId
|
* @param setId
|
||||||
* where meta data has to be deleted
|
* where meta data has to be deleted
|
||||||
@@ -119,7 +131,7 @@ public interface DistributionSetManagement
|
|||||||
void deleteMetaData(long setId, @NotEmpty String key);
|
void deleteMetaData(long setId, @NotEmpty String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves the distribution set for a given action.
|
* Retrieves the distribution set for a given action.
|
||||||
*
|
*
|
||||||
* @param actionId
|
* @param actionId
|
||||||
* the action associated with the distribution set
|
* the action associated with the distribution set
|
||||||
@@ -140,6 +152,7 @@ public interface DistributionSetManagement
|
|||||||
*
|
*
|
||||||
* @param setId
|
* @param setId
|
||||||
* to look for.
|
* to look for.
|
||||||
|
*
|
||||||
* @return {@link DistributionSet}
|
* @return {@link DistributionSet}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||||
@@ -152,13 +165,14 @@ public interface DistributionSetManagement
|
|||||||
* name of {@link DistributionSet}; case insensitive
|
* name of {@link DistributionSet}; case insensitive
|
||||||
* @param version
|
* @param version
|
||||||
* version of {@link DistributionSet}
|
* version of {@link DistributionSet}
|
||||||
|
*
|
||||||
* @return the page with the found {@link DistributionSet}
|
* @return the page with the found {@link DistributionSet}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||||
Optional<DistributionSet> getByNameAndVersion(@NotEmpty String distributionName, @NotEmpty String version);
|
Optional<DistributionSet> getByNameAndVersion(@NotEmpty String distributionName, @NotEmpty String version);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all meta data by the given distribution set id.
|
* Finds all meta data by the given distribution set id.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result
|
* the page request to page the result
|
||||||
@@ -175,7 +189,7 @@ public interface DistributionSetManagement
|
|||||||
Page<DistributionSetMetadata> findMetaDataByDistributionSetId(@NotNull Pageable pageable, long setId);
|
Page<DistributionSetMetadata> findMetaDataByDistributionSetId(@NotNull Pageable pageable, long setId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all meta data by the given distribution set id.
|
* Finds all meta data by the given distribution set id.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result
|
* the page request to page the result
|
||||||
@@ -190,6 +204,7 @@ public interface DistributionSetManagement
|
|||||||
* @throws RSQLParameterUnsupportedFieldException
|
* @throws RSQLParameterUnsupportedFieldException
|
||||||
* if a field in the RSQL string is used but not provided by the
|
* if a field in the RSQL string is used but not provided by the
|
||||||
* given {@code fieldNameProvider}
|
* given {@code fieldNameProvider}
|
||||||
|
*
|
||||||
* @throws RSQLParameterSyntaxException
|
* @throws RSQLParameterSyntaxException
|
||||||
* if the RSQL syntax is wrong
|
* if the RSQL syntax is wrong
|
||||||
*
|
*
|
||||||
@@ -201,7 +216,7 @@ public interface DistributionSetManagement
|
|||||||
@NotNull String rsqlParam);
|
@NotNull String rsqlParam);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all {@link DistributionSet}s.
|
* Finds all {@link DistributionSet}s.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the pagination parameter
|
* the pagination parameter
|
||||||
@@ -210,14 +225,13 @@ public interface DistributionSetManagement
|
|||||||
* sets or <code>false</code> for only incomplete ones nor
|
* sets or <code>false</code> for only incomplete ones nor
|
||||||
* <code>null</code> to return both.
|
* <code>null</code> to return both.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @return all found {@link DistributionSet}s
|
* @return all found {@link DistributionSet}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||||
Page<DistributionSet> findByCompleted(@NotNull Pageable pageable, Boolean complete);
|
Page<DistributionSet> findByCompleted(@NotNull Pageable pageable, Boolean complete);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* method retrieves all {@link DistributionSet}s from the repository in the
|
* Method retrieves all {@link DistributionSet}s from the repository in the
|
||||||
* following order:
|
* following order:
|
||||||
* <p>
|
* <p>
|
||||||
* 1) {@link DistributionSet}s which have the given {@link Target} as
|
* 1) {@link DistributionSet}s which have the given {@link Target} as
|
||||||
@@ -235,6 +249,7 @@ public interface DistributionSetManagement
|
|||||||
* has details of filters to be applied
|
* has details of filters to be applied
|
||||||
* @param assignedOrInstalled
|
* @param assignedOrInstalled
|
||||||
* the id of the Target to be ordered by
|
* the id of the Target to be ordered by
|
||||||
|
*
|
||||||
* @return {@link DistributionSet}s
|
* @return {@link DistributionSet}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||||
@@ -242,7 +257,7 @@ public interface DistributionSetManagement
|
|||||||
@NotNull DistributionSetFilterBuilder distributionSetFilterBuilder, @NotEmpty String assignedOrInstalled);
|
@NotNull DistributionSetFilterBuilder distributionSetFilterBuilder, @NotEmpty String assignedOrInstalled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves {@link DistributionSet}s by filtering on the given parameters.
|
* Retrieves {@link DistributionSet}s by filtering on the given parameters.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* page parameter
|
* page parameter
|
||||||
@@ -255,7 +270,7 @@ public interface DistributionSetManagement
|
|||||||
@NotNull DistributionSetFilter distributionSetFilter);
|
@NotNull DistributionSetFilter distributionSetFilter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves {@link DistributionSet}s by filtering on the given parameters.
|
* Retrieves {@link DistributionSet}s by filtering on the given parameters.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* page parameter
|
* page parameter
|
||||||
@@ -266,6 +281,7 @@ public interface DistributionSetManagement
|
|||||||
* @throws RSQLParameterUnsupportedFieldException
|
* @throws RSQLParameterUnsupportedFieldException
|
||||||
* if a field in the RSQL string is used but not provided by the
|
* if a field in the RSQL string is used but not provided by the
|
||||||
* given {@code fieldNameProvider}
|
* given {@code fieldNameProvider}
|
||||||
|
*
|
||||||
* @throws RSQLParameterSyntaxException
|
* @throws RSQLParameterSyntaxException
|
||||||
* if the RSQL syntax is wrong
|
* if the RSQL syntax is wrong
|
||||||
*
|
*
|
||||||
@@ -276,7 +292,7 @@ public interface DistributionSetManagement
|
|||||||
Page<DistributionSet> findByTag(@NotNull Pageable pageable, long tagId);
|
Page<DistributionSet> findByTag(@NotNull Pageable pageable, long tagId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves {@link DistributionSet}s by filtering on the given parameters.
|
* Retrieves {@link DistributionSet}s by filtering on the given parameters.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* page parameter
|
* page parameter
|
||||||
@@ -293,7 +309,7 @@ public interface DistributionSetManagement
|
|||||||
Page<DistributionSet> findByRsqlAndTag(@NotNull Pageable pageable, @NotNull String rsqlParam, long tagId);
|
Page<DistributionSet> findByRsqlAndTag(@NotNull Pageable pageable, @NotNull String rsqlParam, long tagId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds a single distribution set meta data by its id.
|
* Finds a single distribution set meta data by its id.
|
||||||
*
|
*
|
||||||
* @param setId
|
* @param setId
|
||||||
* of the {@link DistributionSet}
|
* of the {@link DistributionSet}
|
||||||
@@ -375,7 +391,7 @@ public interface DistributionSetManagement
|
|||||||
DistributionSet unAssignTag(long setId, long tagId);
|
DistributionSet unAssignTag(long setId, long tagId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates a distribution set meta data value if corresponding entry exists.
|
* Updates a distribution set meta data value if corresponding entry exists.
|
||||||
*
|
*
|
||||||
* @param setId
|
* @param setId
|
||||||
* {@link DistributionSet} of the meta data entry to be updated
|
* {@link DistributionSet} of the meta data entry to be updated
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
|
|||||||
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
|
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
@@ -63,6 +64,10 @@ public interface DistributionSetTypeManagement
|
|||||||
* @throws EntityReadOnlyException
|
* @throws EntityReadOnlyException
|
||||||
* if the {@link DistributionSetType} while it is already in use
|
* if the {@link DistributionSetType} while it is already in use
|
||||||
* by a {@link DistributionSet}
|
* by a {@link DistributionSet}
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link SoftwareModuleType}s is
|
||||||
|
* exceeded for the addressed {@link DistributionSetType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
DistributionSetType assignOptionalSoftwareModuleTypes(long dsTypeId,
|
DistributionSetType assignOptionalSoftwareModuleTypes(long dsTypeId,
|
||||||
@@ -84,6 +89,10 @@ public interface DistributionSetTypeManagement
|
|||||||
* @throws EntityReadOnlyException
|
* @throws EntityReadOnlyException
|
||||||
* if the {@link DistributionSetType} while it is already in use
|
* if the {@link DistributionSetType} while it is already in use
|
||||||
* by a {@link DistributionSet}
|
* by a {@link DistributionSet}
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link SoftwareModuleType}s is
|
||||||
|
* exceeded for the addressed {@link DistributionSetType}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
DistributionSetType assignMandatorySoftwareModuleTypes(long dsTypeId,
|
DistributionSetType assignMandatorySoftwareModuleTypes(long dsTypeId,
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
|||||||
public interface QuotaManagement {
|
public interface QuotaManagement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Maximum number of {@link ActionStatus} entries that the
|
* @return maximum number of {@link ActionStatus} entries that the
|
||||||
* controller can report for an {@link Action}.
|
* controller can report for an {@link Action}.
|
||||||
*/
|
*/
|
||||||
int getMaxStatusEntriesPerAction();
|
int getMaxStatusEntriesPerAction();
|
||||||
@@ -43,4 +43,57 @@ public interface QuotaManagement {
|
|||||||
*/
|
*/
|
||||||
int getMaxMessagesPerActionStatus();
|
int getMaxMessagesPerActionStatus();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return maximum number of meta data entries per software module
|
||||||
|
*/
|
||||||
|
int getMaxMetaDataEntriesPerSoftwareModule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return maximum number of meta data entries per distribution set
|
||||||
|
*/
|
||||||
|
int getMaxMetaDataEntriesPerDistributionSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return maximum number of software modules per distribution set
|
||||||
|
*/
|
||||||
|
int getMaxSoftwareModulesPerDistributionSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of software module types per distribution set
|
||||||
|
* type
|
||||||
|
*/
|
||||||
|
int getMaxSoftwareModuleTypesPerDistributionSetType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of artifacts per software module
|
||||||
|
*/
|
||||||
|
int getMaxArtifactsPerSoftwareModule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of targets per rollout group
|
||||||
|
*/
|
||||||
|
int getMaxTargetsPerRolloutGroup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of targets which for a manual distribution set
|
||||||
|
* assignment
|
||||||
|
*/
|
||||||
|
int getMaxTargetsPerManualAssignment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of targets for an automatic distribution set
|
||||||
|
* assignment
|
||||||
|
*/
|
||||||
|
int getMaxTargetsPerAutoAssignment();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum number of actions per target
|
||||||
|
*/
|
||||||
|
int getMaxActionsPerTarget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the maximum size of software artifacts in bytes
|
||||||
|
*/
|
||||||
|
long getMaxArtifactSize();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
|||||||
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||||
@@ -123,6 +124,9 @@ public interface RolloutManagement {
|
|||||||
* if given {@link DistributionSet} does not exist
|
* if given {@link DistributionSet} does not exist
|
||||||
* @throws ConstraintViolationException
|
* @throws ConstraintViolationException
|
||||||
* if rollout or group parameters are invalid.
|
* if rollout or group parameters are invalid.
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of allowed targets per rollout group is
|
||||||
|
* exceeded.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
|
||||||
Rollout create(@NotNull RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
|
Rollout create(@NotNull RolloutCreate create, int amountGroup, @NotNull RolloutGroupConditions conditions);
|
||||||
@@ -157,6 +161,9 @@ public interface RolloutManagement {
|
|||||||
* if given {@link DistributionSet} does not exist
|
* if given {@link DistributionSet} does not exist
|
||||||
* @throws ConstraintViolationException
|
* @throws ConstraintViolationException
|
||||||
* if rollout or group parameters are invalid
|
* if rollout or group parameters are invalid
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of allowed targets per rollout group is
|
||||||
|
* exceeded.
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_CREATE)
|
||||||
Rollout create(@NotNull @Valid RolloutCreate rollout, @NotNull @Valid List<RolloutGroupCreate> groups,
|
Rollout create(@NotNull @Valid RolloutCreate rollout, @NotNull @Valid List<RolloutGroupCreate> groups,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataUpdate;
|
|||||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
|
import org.eclipse.hawkbit.repository.builder.SoftwareModuleUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
||||||
@@ -60,37 +61,51 @@ public interface SoftwareModuleManagement
|
|||||||
long countByTextAndType(String searchText, Long typeId);
|
long countByTextAndType(String searchText, Long typeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates a list of software module meta data entries.
|
* Creates a list of software module meta data entries.
|
||||||
*
|
*
|
||||||
* @param metadata
|
* @param metadata
|
||||||
* the meta data entries to create
|
* the meta data entries to create
|
||||||
|
*
|
||||||
* @return the updated or created software module meta data entries
|
* @return the updated or created software module meta data entries
|
||||||
|
*
|
||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* in case one of the meta data entry already exists for the
|
* in case one of the meta data entry already exists for the
|
||||||
* specific key
|
* specific key
|
||||||
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if software module with given ID does not exist
|
* if software module with given ID does not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link SoftwareModuleMetadata}
|
||||||
|
* entries is exceeded for the addressed {@link SoftwareModule}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
List<SoftwareModuleMetadata> createMetaData(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata);
|
List<SoftwareModuleMetadata> createMetaData(@NotNull @Valid Collection<SoftwareModuleMetadataCreate> metadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creates or updates a single software module meta data entry.
|
* Creates or updates a single software module meta data entry.
|
||||||
*
|
*
|
||||||
* @param metadata
|
* @param metadata
|
||||||
* the meta data entry to create
|
* the meta data entry to create
|
||||||
|
*
|
||||||
* @return the updated or created software module meta data entry
|
* @return the updated or created software module meta data entry
|
||||||
|
*
|
||||||
* @throws EntityAlreadyExistsException
|
* @throws EntityAlreadyExistsException
|
||||||
* in case the meta data entry already exists for the specific
|
* in case the meta data entry already exists for the specific
|
||||||
* key
|
* key
|
||||||
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if software module with given ID does not exist
|
* if software module with given ID does not exist
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of {@link SoftwareModuleMetadata}
|
||||||
|
* entries is exceeded for the addressed {@link SoftwareModule}
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||||
SoftwareModuleMetadata createMetaData(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
|
SoftwareModuleMetadata createMetaData(@NotNull @Valid SoftwareModuleMetadataCreate metadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deletes a software module meta data entry.
|
* Deletes a software module meta data entry.
|
||||||
*
|
*
|
||||||
* @param moduleId
|
* @param moduleId
|
||||||
* where meta data has to be deleted
|
* where meta data has to be deleted
|
||||||
@@ -104,12 +119,13 @@ public interface SoftwareModuleManagement
|
|||||||
void deleteMetaData(long moduleId, @NotEmpty String key);
|
void deleteMetaData(long moduleId, @NotEmpty String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns all modules assigned to given {@link DistributionSet}.
|
* Returns all modules assigned to given {@link DistributionSet}.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result set
|
* the page request to page the result set
|
||||||
* @param setId
|
* @param setId
|
||||||
* to search for
|
* to search for
|
||||||
|
*
|
||||||
* @return all {@link SoftwareModule}s that are assigned to given
|
* @return all {@link SoftwareModule}s that are assigned to given
|
||||||
* {@link DistributionSet}.
|
* {@link DistributionSet}.
|
||||||
*
|
*
|
||||||
@@ -130,6 +146,7 @@ public interface SoftwareModuleManagement
|
|||||||
* to be filtered as "like" on {@link SoftwareModule#getName()}
|
* to be filtered as "like" on {@link SoftwareModule#getName()}
|
||||||
* @param typeId
|
* @param typeId
|
||||||
* to be filtered as "like" on {@link SoftwareModule#getType()}
|
* to be filtered as "like" on {@link SoftwareModule#getType()}
|
||||||
|
*
|
||||||
* @return the page of found {@link SoftwareModule}
|
* @return the page of found {@link SoftwareModule}
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -139,7 +156,7 @@ public interface SoftwareModuleManagement
|
|||||||
Slice<SoftwareModule> findByTextAndType(@NotNull Pageable pageable, String searchText, Long typeId);
|
Slice<SoftwareModule> findByTextAndType(@NotNull Pageable pageable, String searchText, Long typeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves {@link SoftwareModule} by their name AND version AND type..
|
* Retrieves {@link SoftwareModule} by their name AND version AND type..
|
||||||
*
|
*
|
||||||
* @param name
|
* @param name
|
||||||
* of the {@link SoftwareModule}
|
* of the {@link SoftwareModule}
|
||||||
@@ -147,6 +164,7 @@ public interface SoftwareModuleManagement
|
|||||||
* of the {@link SoftwareModule}
|
* of the {@link SoftwareModule}
|
||||||
* @param typeId
|
* @param typeId
|
||||||
* of the {@link SoftwareModule}
|
* of the {@link SoftwareModule}
|
||||||
|
*
|
||||||
* @return the found {@link SoftwareModule}
|
* @return the found {@link SoftwareModule}
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -156,7 +174,7 @@ public interface SoftwareModuleManagement
|
|||||||
Optional<SoftwareModule> getByNameAndVersionAndType(@NotEmpty String name, @NotEmpty String version, long typeId);
|
Optional<SoftwareModule> getByNameAndVersionAndType(@NotEmpty String name, @NotEmpty String version, long typeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds a single software module meta data by its id.
|
* Finds a single software module meta data by its id.
|
||||||
*
|
*
|
||||||
* @param moduleId
|
* @param moduleId
|
||||||
* where meta data has to be found
|
* where meta data has to be found
|
||||||
@@ -171,7 +189,7 @@ public interface SoftwareModuleManagement
|
|||||||
Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(long moduleId, @NotEmpty String key);
|
Optional<SoftwareModuleMetadata> getMetaDataBySoftwareModuleId(long moduleId, @NotEmpty String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all meta data by the given software module id.
|
* Finds all meta data by the given software module id.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result
|
* the page request to page the result
|
||||||
@@ -188,7 +206,7 @@ public interface SoftwareModuleManagement
|
|||||||
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(@NotNull Pageable pageable, long moduleId);
|
Page<SoftwareModuleMetadata> findMetaDataBySoftwareModuleId(@NotNull Pageable pageable, long moduleId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all meta data by the given software module id where
|
* Finds all meta data by the given software module id where
|
||||||
* {@link SoftwareModuleMetadata#isTargetVisible()}.
|
* {@link SoftwareModuleMetadata#isTargetVisible()}.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
@@ -207,7 +225,7 @@ public interface SoftwareModuleManagement
|
|||||||
long moduleId);
|
long moduleId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* finds all meta data by the given software module id.
|
* Finds all meta data by the given software module id.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result
|
* the page request to page the result
|
||||||
@@ -222,8 +240,10 @@ public interface SoftwareModuleManagement
|
|||||||
* @throws RSQLParameterUnsupportedFieldException
|
* @throws RSQLParameterUnsupportedFieldException
|
||||||
* if a field in the RSQL string is used but not provided by the
|
* if a field in the RSQL string is used but not provided by the
|
||||||
* given {@code fieldNameProvider}
|
* given {@code fieldNameProvider}
|
||||||
|
*
|
||||||
* @throws RSQLParameterSyntaxException
|
* @throws RSQLParameterSyntaxException
|
||||||
* if the RSQL syntax is wrong
|
* if the RSQL syntax is wrong
|
||||||
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if software module with given ID does not exist
|
* if software module with given ID does not exist
|
||||||
*/
|
*/
|
||||||
@@ -249,6 +269,7 @@ public interface SoftwareModuleManagement
|
|||||||
* filtered as "like" on {@link SoftwareModule#getName()}
|
* filtered as "like" on {@link SoftwareModule#getName()}
|
||||||
* @param typeId
|
* @param typeId
|
||||||
* filtered as "equal" on {@link SoftwareModule#getType()}
|
* filtered as "equal" on {@link SoftwareModule#getType()}
|
||||||
|
*
|
||||||
* @return the page of found {@link SoftwareModule}
|
* @return the page of found {@link SoftwareModule}
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -259,13 +280,14 @@ public interface SoftwareModuleManagement
|
|||||||
@NotNull Pageable pageable, long orderByDistributionId, String searchText, Long typeId);
|
@NotNull Pageable pageable, long orderByDistributionId, String searchText, Long typeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}
|
* Retrieves the {@link SoftwareModule}s by their {@link SoftwareModuleType}
|
||||||
* .
|
* .
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* page parameters
|
* page parameters
|
||||||
* @param typeId
|
* @param typeId
|
||||||
* to be filtered on
|
* to be filtered on
|
||||||
|
*
|
||||||
* @return the found {@link SoftwareModule}s
|
* @return the found {@link SoftwareModule}s
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
@@ -275,7 +297,7 @@ public interface SoftwareModuleManagement
|
|||||||
Slice<SoftwareModule> findByType(@NotNull Pageable pageable, long typeId);
|
Slice<SoftwareModule> findByType(@NotNull Pageable pageable, long typeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates a distribution set meta data value if corresponding entry exists.
|
* Updates a distribution set meta data value if corresponding entry exists.
|
||||||
*
|
*
|
||||||
* @param update
|
* @param update
|
||||||
* the meta data entry to be updated
|
* the meta data entry to be updated
|
||||||
|
|||||||
@@ -18,8 +18,10 @@ import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
|||||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -32,20 +34,26 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
|||||||
public interface TargetFilterQueryManagement {
|
public interface TargetFilterQueryManagement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* creating new {@link TargetFilterQuery}.
|
* Creates a new {@link TargetFilterQuery}.
|
||||||
*
|
*
|
||||||
* @param create
|
* @param create
|
||||||
* to create
|
* to create
|
||||||
* @return the created {@link TargetFilterQuery}
|
*
|
||||||
|
* @return the new {@link TargetFilterQuery}
|
||||||
|
*
|
||||||
* @throws ConstraintViolationException
|
* @throws ConstraintViolationException
|
||||||
* if fields are not filled as specified. Check
|
* if fields are not filled as specified. Check
|
||||||
* {@link TargetFilterQueryCreate} for field constraints.
|
* {@link TargetFilterQueryCreate} for field constraints.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the maximum number of targets that is addressed by the
|
||||||
|
* given query is exceeded (auto-assignments only)
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_CREATE_TARGET)
|
||||||
TargetFilterQuery create(@NotNull @Valid TargetFilterQueryCreate create);
|
TargetFilterQuery create(@NotNull @Valid TargetFilterQueryCreate create);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete target filter query.
|
* Deletes the {@link TargetFilterQuery} with the given ID.
|
||||||
*
|
*
|
||||||
* @param targetFilterQueryId
|
* @param targetFilterQueryId
|
||||||
* IDs of target filter query to be deleted
|
* IDs of target filter query to be deleted
|
||||||
@@ -57,7 +65,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
void delete(long targetFilterQueryId);
|
void delete(long targetFilterQueryId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies provided filter syntax.
|
* Verifies the provided filter syntax.
|
||||||
*
|
*
|
||||||
* @param query
|
* @param query
|
||||||
* to verify
|
* to verify
|
||||||
@@ -67,6 +75,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
* @throws RSQLParameterUnsupportedFieldException
|
* @throws RSQLParameterUnsupportedFieldException
|
||||||
* if a field in the RSQL string is used but not provided by the
|
* if a field in the RSQL string is used but not provided by the
|
||||||
* given {@code fieldNameProvider}
|
* given {@code fieldNameProvider}
|
||||||
|
*
|
||||||
* @throws RSQLParameterSyntaxException
|
* @throws RSQLParameterSyntaxException
|
||||||
* if the RSQL syntax is wrong
|
* if the RSQL syntax is wrong
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +84,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Retrieves all target filter query{@link TargetFilterQuery}.
|
* Retrieves all {@link TargetFilterQuery}s.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
@@ -85,7 +94,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
Page<TargetFilterQuery> findAll(@NotNull Pageable pageable);
|
Page<TargetFilterQuery> findAll(@NotNull Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Counts all target filter queries
|
* Counts all {@link TargetFilterQuery}s.
|
||||||
*
|
*
|
||||||
* @return the number of all target filter queries
|
* @return the number of all target filter queries
|
||||||
*/
|
*/
|
||||||
@@ -93,46 +102,46 @@ public interface TargetFilterQueryManagement {
|
|||||||
long count();
|
long count();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all target filter query which {@link TargetFilterQuery}.
|
* Retrieves all {@link TargetFilterQuery}s which match the given name
|
||||||
*
|
* filter.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
* @param name
|
* @param name
|
||||||
* name filter
|
* name filter
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
* @return the page with the found {@link TargetFilterQuery}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findByName(@NotNull Pageable pageable, @NotNull String name);
|
Page<TargetFilterQuery> findByName(@NotNull Pageable pageable, @NotNull String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all target filter query which {@link TargetFilterQuery}.
|
* Retrieves all {@link TargetFilterQuery} which match the given RSQL
|
||||||
*
|
* filter.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
* @param rsqlFilter
|
* @param rsqlFilter
|
||||||
* RSQL filter string
|
* RSQL filter string
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
* @return the page with the found {@link TargetFilterQuery}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findByRsql(@NotNull Pageable pageable, @NotNull String rsqlFilter);
|
Page<TargetFilterQuery> findByRsql(@NotNull Pageable pageable, @NotNull String rsqlFilter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all target filter query which have exactly the provided query.
|
* Retrieves all {@link TargetFilterQuery}s which match the given query.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
* @param query
|
* @param query
|
||||||
* the query saved in the target filter query
|
* the query saved in the target filter query
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
* @return the page with the found {@link TargetFilterQuery}s
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findByQuery(@NotNull Pageable pageable, @NotNull String query);
|
Page<TargetFilterQuery> findByQuery(@NotNull Pageable pageable, @NotNull String query);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all target filter query which {@link TargetFilterQuery}.
|
* Retrieves all {@link TargetFilterQuery}s which match the given
|
||||||
*
|
* auto-assign distribution set and RSQL filter.
|
||||||
*
|
*
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* pagination parameter
|
* pagination parameter
|
||||||
@@ -140,28 +149,26 @@ public interface TargetFilterQueryManagement {
|
|||||||
* the auto assign distribution set
|
* the auto assign distribution set
|
||||||
* @param rsqlParam
|
* @param rsqlParam
|
||||||
* RSQL filter
|
* RSQL filter
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
* @return the page with the found {@link TargetFilterQuery}s
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if DS with given ID does not exist
|
* if DS with given ID does not exist
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findByAutoAssignDSAndRsql(@NotNull Pageable pageable, long setId,
|
Page<TargetFilterQuery> findByAutoAssignDSAndRsql(@NotNull Pageable pageable, long setId, String rsqlParam);
|
||||||
String rsqlParam);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all target filter query with auto assign DS which
|
* Retrieves all {@link TargetFilterQuery}s with an auto-assign distribution
|
||||||
* {@link TargetFilterQuery}.
|
* set.
|
||||||
*
|
*
|
||||||
*
|
* @return the page with the found {@link TargetFilterQuery}s
|
||||||
* @return the page with the found {@link TargetFilterQuery}
|
|
||||||
* @param pageable
|
* @param pageable
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||||
Page<TargetFilterQuery> findWithAutoAssignDS(@NotNull Pageable pageable);
|
Page<TargetFilterQuery> findWithAutoAssignDS(@NotNull Pageable pageable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find target filter query by id.
|
* Finds the {@link TargetFilterQuery} by id.
|
||||||
*
|
*
|
||||||
* @param targetFilterQueryId
|
* @param targetFilterQueryId
|
||||||
* Target filter query id
|
* Target filter query id
|
||||||
@@ -172,7 +179,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
Optional<TargetFilterQuery> get(long targetFilterQueryId);
|
Optional<TargetFilterQuery> get(long targetFilterQueryId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find target filter query by name.
|
* Finds the {@link TargetFilterQuery} that matches the given name.
|
||||||
*
|
*
|
||||||
* @param targetFilterQueryName
|
* @param targetFilterQueryName
|
||||||
* Target filter query name
|
* Target filter query name
|
||||||
@@ -183,7 +190,7 @@ public interface TargetFilterQueryManagement {
|
|||||||
Optional<TargetFilterQuery> getByName(@NotNull String targetFilterQueryName);
|
Optional<TargetFilterQuery> getByName(@NotNull String targetFilterQueryName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates the {@link TargetFilterQuery}.
|
* Updates the {@link TargetFilterQuery}.
|
||||||
*
|
*
|
||||||
* @param update
|
* @param update
|
||||||
* to be updated
|
* to be updated
|
||||||
@@ -193,25 +200,36 @@ public interface TargetFilterQueryManagement {
|
|||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either {@link TargetFilterQuery} and/or autoAssignDs are
|
* if either {@link TargetFilterQuery} and/or autoAssignDs are
|
||||||
* provided but not found
|
* provided but not found
|
||||||
|
*
|
||||||
* @throws ConstraintViolationException
|
* @throws ConstraintViolationException
|
||||||
* if fields are not filled as specified. Check
|
* if fields are not filled as specified. Check
|
||||||
* {@link TargetFilterQueryUpdate} for field constraints.
|
* {@link TargetFilterQueryUpdate} for field constraints.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the update contains a new query which addresses too many
|
||||||
|
* targets (auto-assignments only)
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||||
TargetFilterQuery update(@NotNull @Valid TargetFilterQueryUpdate update);
|
TargetFilterQuery update(@NotNull @Valid TargetFilterQueryUpdate update);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* updates the {@link TargetFilterQuery#getAutoAssignDistributionSet()}.
|
* Updates the the auto-assign {@link DistributionSet} of the addressed
|
||||||
|
* {@link TargetFilterQuery}.
|
||||||
*
|
*
|
||||||
* @param queryId
|
* @param queryId
|
||||||
* to be updated
|
* of the target filter query to be updated
|
||||||
* @param dsId
|
* @param dsId
|
||||||
* to be updated or <code>null</code> in order to remove it
|
* to be updated or <code>null</code> in order to remove it
|
||||||
|
*
|
||||||
* @return the updated {@link TargetFilterQuery}
|
* @return the updated {@link TargetFilterQuery}
|
||||||
*
|
*
|
||||||
* @throws EntityNotFoundException
|
* @throws EntityNotFoundException
|
||||||
* if either {@link TargetFilterQuery} and/or autoAssignDs are
|
* if either {@link TargetFilterQuery} and/or autoAssignDs are
|
||||||
* provided but not found
|
* provided but not found
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the query that is already associated with this filter
|
||||||
|
* query addresses too many targets (auto-assignments only)
|
||||||
*/
|
*/
|
||||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||||
TargetFilterQuery updateAutoAssignDS(long queryId, Long dsId);
|
TargetFilterQuery updateAutoAssignDS(long queryId, Long dsId);
|
||||||
|
|||||||
@@ -17,8 +17,11 @@ import org.eclipse.hawkbit.repository.model.BaseEntity;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public final class QuotaExceededException extends AbstractServerRtException {
|
public final class QuotaExceededException extends AbstractServerRtException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final String ASSIGNMENT_QUOTA_EXCEEDED_MESSAGE = "Quota exceeded: Cannot assign %s more %s entities to %s '%s'. The maximum is %s.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new QuotaExceededException with
|
* Creates a new QuotaExceededException with
|
||||||
* {@link SpServerError#SP_QUOTA_EXCEEDED} error.
|
* {@link SpServerError#SP_QUOTA_EXCEEDED} error.
|
||||||
@@ -28,11 +31,26 @@ public final class QuotaExceededException extends AbstractServerRtException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Creates a new QuotaExceededException with a custom error message.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The custom error message.
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final String message) {
|
||||||
|
super(message, SpServerError.SP_QUOTA_EXCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a QuotaExceededException with a custom error message and a root
|
||||||
|
* cause.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The custom error message.
|
||||||
* @param cause
|
* @param cause
|
||||||
* for the exception
|
* for the exception
|
||||||
*/
|
*/
|
||||||
public QuotaExceededException(final Throwable cause) {
|
public QuotaExceededException(final String message, final Throwable cause) {
|
||||||
super(SpServerError.SP_QUOTA_EXCEEDED, cause);
|
super(message, SpServerError.SP_QUOTA_EXCEEDED, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +75,55 @@ public final class QuotaExceededException extends AbstractServerRtException {
|
|||||||
* that is defined by the repository
|
* that is defined by the repository
|
||||||
*/
|
*/
|
||||||
public QuotaExceededException(final String type, final long inserted, final int quota) {
|
public QuotaExceededException(final String type, final long inserted, final int quota) {
|
||||||
super("Request contains too many entries of {" + type + "}. {" + inserted + "} is bejond the permitted {"
|
super("Request contains too many entries of {" + type + "}. {" + inserted + "} is beyond the permitted {"
|
||||||
+ quota + "}.", SpServerError.SP_QUOTA_EXCEEDED);
|
+ quota + "}.", SpServerError.SP_QUOTA_EXCEEDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a QuotaExceededException which is to be thrown when an assignment
|
||||||
|
* quota is exceeded.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* The type of the entities that shall be assigned to the
|
||||||
|
* specified parent entity.
|
||||||
|
* @param parentType
|
||||||
|
* The type of the parent entity.
|
||||||
|
* @param parentId
|
||||||
|
* The ID of the parent entity.
|
||||||
|
* @param requested
|
||||||
|
* The number of entities that shall be assigned to the specified
|
||||||
|
* parent entity.
|
||||||
|
* @param quota
|
||||||
|
* The maximum number of entities that can be assigned to the
|
||||||
|
* parent entity.
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final Class<?> type, final Class<?> parentType, final Long parentId,
|
||||||
|
final long requested, final long quota) {
|
||||||
|
this(type.getSimpleName(), parentType.getSimpleName(), parentId, requested, quota);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a QuotaExceededException which is to be thrown when an assignment
|
||||||
|
* quota is exceeded.
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* The type of the entities that shall be assigned to the
|
||||||
|
* specified parent entity.
|
||||||
|
* @param parentType
|
||||||
|
* The type of the parent entity.
|
||||||
|
* @param parentId
|
||||||
|
* The ID of the parent entity.
|
||||||
|
* @param requested
|
||||||
|
* The number of entities that shall be assigned to the specified
|
||||||
|
* parent entity.
|
||||||
|
* @param quota
|
||||||
|
* The maximum number of entities that can be assigned to the
|
||||||
|
* parent entity.
|
||||||
|
*/
|
||||||
|
public QuotaExceededException(final String type, final String parentType, final Long parentId, final long requested,
|
||||||
|
final long quota) {
|
||||||
|
super(String.format(ASSIGNMENT_QUOTA_EXCEEDED_MESSAGE, requested, type, parentType,
|
||||||
|
parentId != null ? String.valueOf(parentId) : "<new>", quota), SpServerError.SP_QUOTA_EXCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ public class RolloutGroupsValidation {
|
|||||||
/**
|
/**
|
||||||
* The total amount of targets in a {@link Rollout}
|
* The total amount of targets in a {@link Rollout}
|
||||||
*/
|
*/
|
||||||
private long totalTargets;
|
private final long totalTargets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list containing the count of targets for each {@link RolloutGroup}
|
* A list containing the count of targets for each {@link RolloutGroup}
|
||||||
*/
|
*/
|
||||||
private List<Long> targetsPerGroup;
|
private final List<Long> targetsPerGroup;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new validation result
|
* Instantiates a new validation result
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ public final class DurationHelper {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static final class DurationRangeValidator {
|
public static final class DurationRangeValidator {
|
||||||
final Duration min;
|
private final Duration min;
|
||||||
final Duration max;
|
private final Duration max;
|
||||||
|
|
||||||
private DurationRangeValidator(final Duration min, final Duration max) {
|
private DurationRangeValidator(final Duration min, final Duration max) {
|
||||||
this.min = min;
|
this.min = min;
|
||||||
|
|||||||
@@ -48,4 +48,54 @@ public class PropertiesQuotaManagement implements QuotaManagement {
|
|||||||
return securityProperties.getDos().getMaxMessagesPerActionStatus();
|
return securityProperties.getDos().getMaxMessagesPerActionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxMetaDataEntriesPerSoftwareModule() {
|
||||||
|
return securityProperties.getDos().getMaxMetaDataEntriesPerSoftwareModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxMetaDataEntriesPerDistributionSet() {
|
||||||
|
return securityProperties.getDos().getMaxMetaDataEntriesPerDistributionSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxSoftwareModulesPerDistributionSet() {
|
||||||
|
return securityProperties.getDos().getMaxSoftwareModulesPerDistributionSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxSoftwareModuleTypesPerDistributionSetType() {
|
||||||
|
return securityProperties.getDos().getMaxSoftwareModuleTypesPerDistributionSetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxArtifactsPerSoftwareModule() {
|
||||||
|
return securityProperties.getDos().getMaxArtifactsPerSoftwareModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxTargetsPerRolloutGroup() {
|
||||||
|
return securityProperties.getDos().getMaxTargetsPerRolloutGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxActionsPerTarget() {
|
||||||
|
return securityProperties.getDos().getMaxActionsPerTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxTargetsPerManualAssignment() {
|
||||||
|
return securityProperties.getDos().getMaxTargetsPerManualAssignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxTargetsPerAutoAssignment() {
|
||||||
|
return securityProperties.getDos().getMaxTargetsPerAutoAssignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaxArtifactSize() {
|
||||||
|
return securityProperties.getDos().getMaxArtifactSize();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import javax.validation.ValidationException;
|
import javax.validation.ValidationException;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
@@ -75,9 +76,11 @@ public final class RolloutHelper {
|
|||||||
*/
|
*/
|
||||||
public static void verifyRolloutGroupParameter(final int amountGroup, final QuotaManagement quotaManagement) {
|
public static void verifyRolloutGroupParameter(final int amountGroup, final QuotaManagement quotaManagement) {
|
||||||
if (amountGroup <= 0) {
|
if (amountGroup <= 0) {
|
||||||
throw new ValidationException("the amount of groups cannot be lower than zero");
|
throw new ValidationException("The amount of groups cannot be lower than zero");
|
||||||
} else if (amountGroup > quotaManagement.getMaxRolloutGroupsPerRollout()) {
|
} else if (amountGroup > quotaManagement.getMaxRolloutGroupsPerRollout()) {
|
||||||
throw new ValidationException("the amount of groups cannot be greater than 500");
|
throw new QuotaExceededException(
|
||||||
|
"The amount of groups cannot be greater than " + quotaManagement.getMaxRolloutGroupsPerRollout());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +92,9 @@ public final class RolloutHelper {
|
|||||||
*/
|
*/
|
||||||
public static void verifyRolloutGroupTargetPercentage(final float percentage) {
|
public static void verifyRolloutGroupTargetPercentage(final float percentage) {
|
||||||
if (percentage <= 0) {
|
if (percentage <= 0) {
|
||||||
throw new ValidationException("the percentage must be greater than zero");
|
throw new ValidationException("The percentage must be greater than zero");
|
||||||
} else if (percentage > 100) {
|
} else if (percentage > 100) {
|
||||||
throw new ValidationException("the percentage must not be greater than 100");
|
throw new ValidationException("The percentage must not be greater than 100");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||||
@@ -24,6 +25,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
@@ -46,17 +48,19 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
protected final ApplicationContext applicationContext;
|
protected final ApplicationContext applicationContext;
|
||||||
private final ActionRepository actionRepository;
|
private final ActionRepository actionRepository;
|
||||||
private final ActionStatusRepository actionStatusRepository;
|
private final ActionStatusRepository actionStatusRepository;
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
|
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||||
final ActionStatusRepository actionStatusRepository) {
|
final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) {
|
||||||
this.targetRepository = targetRepository;
|
this.targetRepository = targetRepository;
|
||||||
this.afterCommit = afterCommit;
|
this.afterCommit = afterCommit;
|
||||||
this.eventPublisher = eventPublisher;
|
this.eventPublisher = eventPublisher;
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.actionRepository = actionRepository;
|
this.actionRepository = actionRepository;
|
||||||
this.actionStatusRepository = actionStatusRepository;
|
this.actionStatusRepository = actionStatusRepository;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,6 +217,11 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
|
|
||||||
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
||||||
final JpaDistributionSet set) {
|
final JpaDistributionSet set) {
|
||||||
|
|
||||||
|
// enforce the 'max actions per target' quota
|
||||||
|
assertActionsPerTargetQuota(target, 1);
|
||||||
|
|
||||||
|
// create the action
|
||||||
final JpaAction actionForTarget = new JpaAction();
|
final JpaAction actionForTarget = new JpaAction();
|
||||||
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
||||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||||
@@ -237,4 +246,11 @@ public abstract class AbstractDsAssignmentStrategy {
|
|||||||
|
|
||||||
return actionStatus;
|
return actionStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertActionsPerTargetQuota(final Target target, final int requested) {
|
||||||
|
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||||
|
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class,
|
||||||
|
actionRepository::countByTargetId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,16 @@ public interface ActionStatusRepository
|
|||||||
*/
|
*/
|
||||||
Long countByAction(JpaAction action);
|
Long countByAction(JpaAction action);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts {@link ActionStatus} entries of given {@link Action} in
|
||||||
|
* repository.
|
||||||
|
*
|
||||||
|
* @param actionId
|
||||||
|
* of the action to count status entries for
|
||||||
|
* @return number of actions in repository
|
||||||
|
*/
|
||||||
|
long countByActionId(Long actionId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all {@link ActionStatus} entries from repository of given
|
* Retrieves all {@link ActionStatus} entries from repository of given
|
||||||
* ActionId.
|
* ActionId.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
|||||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,4 +24,14 @@ public interface DistributionSetMetadataRepository
|
|||||||
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
|
extends PagingAndSortingRepository<JpaDistributionSetMetadata, DsMetadataCompositeKey>,
|
||||||
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
|
JpaSpecificationExecutor<JpaDistributionSetMetadata> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the meta data entries that match the given distribution set ID.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* of the distribution set.
|
||||||
|
*
|
||||||
|
* @return The number of matching meta data entries.
|
||||||
|
*/
|
||||||
|
long countByDistributionSetId(@Param("id") Long id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import javax.persistence.EntityManager;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -40,6 +41,7 @@ public interface DistributionSetTypeRepository
|
|||||||
* @param isDeleted
|
* @param isDeleted
|
||||||
* to <code>true</code> if only soft deleted entries of
|
* to <code>true</code> if only soft deleted entries of
|
||||||
* <code>false</code> if undeleted ones
|
* <code>false</code> if undeleted ones
|
||||||
|
*
|
||||||
* @return list of found {@link DistributionSetType}s
|
* @return list of found {@link DistributionSetType}s
|
||||||
*/
|
*/
|
||||||
Page<JpaDistributionSetType> findByDeleted(Pageable pageable, boolean isDeleted);
|
Page<JpaDistributionSetType> findByDeleted(Pageable pageable, boolean isDeleted);
|
||||||
@@ -47,7 +49,8 @@ public interface DistributionSetTypeRepository
|
|||||||
/**
|
/**
|
||||||
* @param isDeleted
|
* @param isDeleted
|
||||||
* to <code>true</code> if only marked as deleted have to be
|
* to <code>true</code> if only marked as deleted have to be
|
||||||
* count or all undeleted.
|
* counted or all undeleted.
|
||||||
|
*
|
||||||
* @return number of {@link DistributionSetType}s in the repository.
|
* @return number of {@link DistributionSetType}s in the repository.
|
||||||
*/
|
*/
|
||||||
long countByDeleted(boolean isDeleted);
|
long countByDeleted(boolean isDeleted);
|
||||||
@@ -59,6 +62,7 @@ public interface DistributionSetTypeRepository
|
|||||||
* @param softwareModuleType
|
* @param softwareModuleType
|
||||||
* the software module type to count the distribution set type
|
* the software module type to count the distribution set type
|
||||||
* which has this software module type assigned
|
* which has this software module type assigned
|
||||||
|
*
|
||||||
* @return the number of {@link DistributionSetType}s in the repository
|
* @return the number of {@link DistributionSetType}s in the repository
|
||||||
* assigned to the given software module type
|
* assigned to the given software module type
|
||||||
*/
|
*/
|
||||||
@@ -78,8 +82,29 @@ public interface DistributionSetTypeRepository
|
|||||||
@Query("DELETE FROM JpaDistributionSetType t WHERE t.tenant = :tenant")
|
@Query("DELETE FROM JpaDistributionSetType t WHERE t.tenant = :tenant")
|
||||||
void deleteByTenant(@Param("tenant") String tenant);
|
void deleteByTenant(@Param("tenant") String tenant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the {@link DistributionSetType}s for the given IDs. Workaround
|
||||||
|
* for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
||||||
|
*
|
||||||
|
* @param ids
|
||||||
|
* of the types to be located
|
||||||
|
*
|
||||||
|
* @return a list of distribution set types
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
// Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477
|
|
||||||
@Query("SELECT d FROM JpaDistributionSetType d WHERE d.id IN ?1")
|
@Query("SELECT d FROM JpaDistributionSetType d WHERE d.id IN ?1")
|
||||||
List<JpaDistributionSetType> findAll(Iterable<Long> ids);
|
List<JpaDistributionSetType> findAll(Iterable<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the {@link SoftwareModuleType}s which are associated with the
|
||||||
|
* addressed {@link DistributionSetType}.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* of the distribution set type
|
||||||
|
*
|
||||||
|
* @return the number of associated software module types
|
||||||
|
*/
|
||||||
|
@Query("SELECT COUNT (e.smType) FROM DistributionSetTypeElement e WHERE e.dsType.id = :id")
|
||||||
|
long countSmTypesById(@Param("id") Long id);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,18 @@ import org.eclipse.hawkbit.artifact.repository.HashNotMatchException;
|
|||||||
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
|
||||||
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactDeleteFailedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
|
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
|
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||||
@@ -49,6 +52,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class);
|
private static final Logger LOG = LoggerFactory.getLogger(JpaArtifactManagement.class);
|
||||||
|
|
||||||
|
private static final String MAX_ARTIFACT_SIZE_EXCEEDED = "Quota exceeded: The artifact '%s' (%s bytes) which has been uploaded for software module '%s' exceeds the maximum artifact size of %s bytes.";
|
||||||
|
|
||||||
private final LocalArtifactRepository localArtifactRepository;
|
private final LocalArtifactRepository localArtifactRepository;
|
||||||
|
|
||||||
private final SoftwareModuleRepository softwareModuleRepository;
|
private final SoftwareModuleRepository softwareModuleRepository;
|
||||||
@@ -57,12 +62,15 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
|
|
||||||
private final TenantAware tenantAware;
|
private final TenantAware tenantAware;
|
||||||
|
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
JpaArtifactManagement(final LocalArtifactRepository localArtifactRepository,
|
JpaArtifactManagement(final LocalArtifactRepository localArtifactRepository,
|
||||||
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
|
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
|
||||||
final TenantAware tenantAware) {
|
final QuotaManagement quotaManagement, final TenantAware tenantAware) {
|
||||||
this.localArtifactRepository = localArtifactRepository;
|
this.localArtifactRepository = localArtifactRepository;
|
||||||
this.softwareModuleRepository = softwareModuleRepository;
|
this.softwareModuleRepository = softwareModuleRepository;
|
||||||
this.artifactRepository = artifactRepository;
|
this.artifactRepository = artifactRepository;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
this.tenantAware = tenantAware;
|
this.tenantAware = tenantAware;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,13 +95,16 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Artifact create(final InputStream stream, final long moduleId, final String filename,
|
public Artifact create(final InputStream stream, final long moduleId, final String filename,
|
||||||
final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting,
|
final String providedMd5Sum, final String providedSha1Sum, final boolean overrideExisting,
|
||||||
final String contentType) {
|
final String contentType, final long filesize) {
|
||||||
AbstractDbArtifact result = null;
|
AbstractDbArtifact result = null;
|
||||||
|
|
||||||
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
|
final SoftwareModule softwareModule = getModuleAndThrowExceptionIfThatFails(moduleId);
|
||||||
|
|
||||||
final Artifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
|
final Artifact existing = checkForExistingArtifact(filename, overrideExisting, softwareModule);
|
||||||
|
|
||||||
|
assertArtifactQuota(moduleId, 1);
|
||||||
|
assertMaxArtifactSizeQuota(filename, moduleId, filesize);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = artifactRepository.store(tenantAware.getCurrentTenant(), stream, filename, contentType,
|
result = artifactRepository.store(tenantAware.getCurrentTenant(), stream, filename, contentType,
|
||||||
new DbArtifactHash(providedSha1Sum, providedMd5Sum));
|
new DbArtifactHash(providedSha1Sum, providedMd5Sum));
|
||||||
@@ -113,6 +124,23 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
return storeArtifactMetadata(softwareModule, filename, result, existing);
|
return storeArtifactMetadata(softwareModule, filename, result, existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertArtifactQuota(final long id, final int requested) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxArtifactsPerSoftwareModule(),
|
||||||
|
Artifact.class, SoftwareModule.class, localArtifactRepository::countBySoftwareModuleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMaxArtifactSizeQuota(final String filename, final long id, final long artifactSize) {
|
||||||
|
final long maxArtifactSize = quotaManagement.getMaxArtifactSize();
|
||||||
|
if (maxArtifactSize <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (artifactSize > maxArtifactSize) {
|
||||||
|
final String msg = String.format(MAX_ARTIFACT_SIZE_EXCEEDED, filename, artifactSize, id, maxArtifactSize);
|
||||||
|
LOG.warn(msg);
|
||||||
|
throw new QuotaExceededException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
@@ -207,8 +235,8 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
|||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public Artifact create(final InputStream inputStream, final long moduleId, final String filename,
|
public Artifact create(final InputStream inputStream, final long moduleId, final String filename,
|
||||||
final boolean overrideExisting) {
|
final boolean overrideExisting, final long filesize) {
|
||||||
return create(inputStream, moduleId, filename, null, null, overrideExisting, null);
|
return create(inputStream, moduleId, filename, null, null, overrideExisting, null, filesize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
|
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusCreate;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
@@ -56,6 +55,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||||
@@ -535,8 +535,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// information status entry - check for a potential DOS attack
|
// information status entry - check for a potential DOS attack
|
||||||
checkForTooManyStatusEntries(action);
|
assertActionStatusQuota(action);
|
||||||
checkForTooManyStatusMessages(actionStatus);
|
assertActionStatusMessageQuota(actionStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,12 +546,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForTooManyStatusMessages(final JpaActionStatus actionStatus) {
|
private void assertActionStatusMessageQuota(final JpaActionStatus actionStatus) {
|
||||||
if (actionStatus.getMessages().size() > quotaManagement.getMaxMessagesPerActionStatus()) {
|
QuotaHelper.assertAssignmentQuota(actionStatus.getId(), actionStatus.getMessages().size(),
|
||||||
throw new QuotaExceededException("ActionStatus messages", actionStatus.getMessages().size(),
|
quotaManagement.getMaxMessagesPerActionStatus(), "Message", ActionStatus.class.getSimpleName(), null);
|
||||||
quotaManagement.getMaxStatusEntriesPerAction());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFinishedCancelation(final JpaActionStatus actionStatus, final JpaAction action) {
|
private void handleFinishedCancelation(final JpaActionStatus actionStatus, final JpaAction action) {
|
||||||
@@ -607,8 +604,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// information status entry - check for a potential DOS attack
|
// information status entry - check for a potential DOS attack
|
||||||
checkForTooManyStatusEntries(action);
|
assertActionStatusQuota(action);
|
||||||
checkForTooManyStatusMessages(actionStatus);
|
assertActionStatusMessageQuota(actionStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,16 +625,9 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
targetRepository.save(mergedTarget);
|
targetRepository.save(mergedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForTooManyStatusEntries(final JpaAction action) {
|
private void assertActionStatusQuota(final JpaAction action) {
|
||||||
if (quotaManagement.getMaxStatusEntriesPerAction() > 0) {
|
QuotaHelper.assertAssignmentQuota(action.getId(), 1, quotaManagement.getMaxStatusEntriesPerAction(),
|
||||||
|
ActionStatus.class, Action.class, actionStatusRepository::countByActionId);
|
||||||
final Long statusCount = actionStatusRepository.countByAction(action);
|
|
||||||
|
|
||||||
if (statusCount >= quotaManagement.getMaxStatusEntriesPerAction()) {
|
|
||||||
throw new QuotaExceededException(ActionStatus.class, statusCount + 1,
|
|
||||||
quotaManagement.getMaxStatusEntriesPerAction());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleFinishedAndStoreInTargetStatus(final JpaAction action) {
|
private void handleFinishedAndStoreInTargetStatus(final JpaAction action) {
|
||||||
@@ -694,16 +684,17 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
// unknown update mode
|
// unknown update mode
|
||||||
throw new IllegalStateException("The update mode " + updateMode + " is not supported.");
|
throw new IllegalStateException("The update mode " + updateMode + " is not supported.");
|
||||||
}
|
}
|
||||||
|
assertTargetAttributesQuota(target);
|
||||||
final int attributeCount = controllerAttributes.size();
|
|
||||||
if (attributeCount > quotaManagement.getMaxAttributeEntriesPerTarget()) {
|
|
||||||
throw new QuotaExceededException("Controller attributes", attributeCount,
|
|
||||||
quotaManagement.getMaxAttributeEntriesPerTarget());
|
|
||||||
}
|
|
||||||
|
|
||||||
return targetRepository.save(target);
|
return targetRepository.save(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertTargetAttributesQuota(final JpaTarget target) {
|
||||||
|
final int limit = quotaManagement.getMaxAttributeEntriesPerTarget();
|
||||||
|
QuotaHelper.assertAssignmentQuota(target.getId(), target.getControllerAttributes().size(), limit, "Attribute",
|
||||||
|
Target.class.getSimpleName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
@@ -775,8 +766,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
|||||||
final JpaActionStatus statusMessage = create.build();
|
final JpaActionStatus statusMessage = create.build();
|
||||||
statusMessage.setAction(action);
|
statusMessage.setAction(action);
|
||||||
|
|
||||||
checkForTooManyStatusEntries(action);
|
assertActionStatusQuota(action);
|
||||||
checkForTooManyStatusMessages(statusMessage);
|
assertActionStatusMessageQuota(statusMessage);
|
||||||
|
|
||||||
return actionStatusRepository.save(statusMessage);
|
return actionStatusRepository.save(statusMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import javax.persistence.criteria.Root;
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.repository.ActionFields;
|
import org.eclipse.hawkbit.repository.ActionFields;
|
||||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||||
@@ -46,6 +47,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
@@ -115,6 +117,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
|
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
|
||||||
private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy;
|
private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy;
|
||||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
private final SystemSecurityContext systemSecurityContext;
|
private final SystemSecurityContext systemSecurityContext;
|
||||||
private final Database database;
|
private final Database database;
|
||||||
|
|
||||||
@@ -124,7 +127,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
final AuditorAware<String> auditorProvider, final ApplicationEventPublisher eventPublisher,
|
final AuditorAware<String> auditorProvider, final ApplicationEventPublisher eventPublisher,
|
||||||
final ApplicationContext applicationContext, final AfterTransactionCommitExecutor afterCommit,
|
final ApplicationContext applicationContext, final AfterTransactionCommitExecutor afterCommit,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||||
final SystemSecurityContext systemSecurityContext, final Database database) {
|
final SystemSecurityContext systemSecurityContext, final Database database) {
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
this.actionRepository = actionRepository;
|
this.actionRepository = actionRepository;
|
||||||
@@ -139,10 +142,11 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||||
this.txManager = txManager;
|
this.txManager = txManager;
|
||||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
||||||
applicationContext, actionRepository, actionStatusRepository);
|
applicationContext, actionRepository, actionStatusRepository, quotaManagement);
|
||||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
||||||
applicationContext, actionRepository, actionStatusRepository);
|
applicationContext, actionRepository, actionStatusRepository, quotaManagement);
|
||||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
this.systemSecurityContext = systemSecurityContext;
|
this.systemSecurityContext = systemSecurityContext;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
@@ -215,8 +219,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
* a list of all targets and their action type
|
* a list of all targets and their action type
|
||||||
* @param actionMessage
|
* @param actionMessage
|
||||||
* an optional message to be written into the action status
|
* an optional message to be written into the action status
|
||||||
* @param offline
|
* @param assignmentStrategy
|
||||||
* to <code>true</code> in offline case
|
* the assignment strategy (online /offline)
|
||||||
* @return the assignment result
|
* @return the assignment result
|
||||||
*
|
*
|
||||||
* @throw IncompleteDistributionSetException if mandatory
|
* @throw IncompleteDistributionSetException if mandatory
|
||||||
@@ -240,6 +244,11 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
final List<String> controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
|
final List<String> controllerIDs = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// enforce the 'max targets per manual assignment' quota
|
||||||
|
if (!controllerIDs.isEmpty()) {
|
||||||
|
assertMaxTargetsPerManualAssignmentQuota(set.getId(), controllerIDs.size());
|
||||||
|
}
|
||||||
|
|
||||||
LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size());
|
LOG.debug("assignDistribution({}) to {} targets", set, controllerIDs.size());
|
||||||
|
|
||||||
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
|
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
|
||||||
@@ -318,6 +327,20 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
|||||||
targetManagement);
|
targetManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||||
|
* manual {@link DistributionSet} assignment.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* of the distribution set
|
||||||
|
* @param requested
|
||||||
|
* number of targets to check
|
||||||
|
*/
|
||||||
|
private void assertMaxTargetsPerManualAssignmentQuota(final Long id, final int requested) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxTargetsPerManualAssignment(),
|
||||||
|
Target.class, DistributionSet.class, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
|||||||
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
|
import org.eclipse.hawkbit.repository.DistributionSetMetadataFields;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||||
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
|
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
|
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
|
||||||
@@ -43,6 +44,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
|
import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
|
||||||
@@ -91,6 +93,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
|
|
||||||
private final DistributionSetTypeManagement distributionSetTypeManagement;
|
private final DistributionSetTypeManagement distributionSetTypeManagement;
|
||||||
|
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
private final DistributionSetMetadataRepository distributionSetMetadataRepository;
|
private final DistributionSetMetadataRepository distributionSetMetadataRepository;
|
||||||
|
|
||||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||||
@@ -118,7 +122,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
JpaDistributionSetManagement(final EntityManager entityManager,
|
JpaDistributionSetManagement(final EntityManager entityManager,
|
||||||
final DistributionSetRepository distributionSetRepository,
|
final DistributionSetRepository distributionSetRepository,
|
||||||
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
|
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
|
||||||
final DistributionSetTypeManagement distributionSetTypeManagement,
|
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
|
||||||
final DistributionSetMetadataRepository distributionSetMetadataRepository,
|
final DistributionSetMetadataRepository distributionSetMetadataRepository,
|
||||||
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
|
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
|
||||||
final NoCountPagingRepository criteriaNoCountDao, final ApplicationEventPublisher eventPublisher,
|
final NoCountPagingRepository criteriaNoCountDao, final ApplicationEventPublisher eventPublisher,
|
||||||
@@ -132,6 +136,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
this.distributionSetTagManagement = distributionSetTagManagement;
|
this.distributionSetTagManagement = distributionSetTagManagement;
|
||||||
this.systemManagement = systemManagement;
|
this.systemManagement = systemManagement;
|
||||||
this.distributionSetTypeManagement = distributionSetTypeManagement;
|
this.distributionSetTypeManagement = distributionSetTypeManagement;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
this.distributionSetMetadataRepository = distributionSetMetadataRepository;
|
this.distributionSetMetadataRepository = distributionSetMetadataRepository;
|
||||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||||
this.actionRepository = actionRepository;
|
this.actionRepository = actionRepository;
|
||||||
@@ -317,6 +322,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
assertDistributionSetIsNotAssignedToTargets(setId);
|
assertDistributionSetIsNotAssignedToTargets(setId);
|
||||||
|
|
||||||
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
|
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
|
||||||
|
|
||||||
|
assertSoftwareModuleQuota(setId, modules.size());
|
||||||
|
|
||||||
modules.forEach(set::addModule);
|
modules.forEach(set::addModule);
|
||||||
|
|
||||||
return distributionSetRepository.save(set);
|
return distributionSetRepository.save(set);
|
||||||
@@ -452,6 +460,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists(
|
md.forEach(meta -> checkAndThrowAlreadyIfDistributionSetMetadataExists(
|
||||||
new DsMetadataCompositeKey(dsId, meta.getKey())));
|
new DsMetadataCompositeKey(dsId, meta.getKey())));
|
||||||
|
|
||||||
|
assertMetaDataQuota(dsId, md.size());
|
||||||
|
|
||||||
final JpaDistributionSet set = touch(dsId);
|
final JpaDistributionSet set = touch(dsId);
|
||||||
|
|
||||||
return Collections.unmodifiableList(md.stream()
|
return Collections.unmodifiableList(md.stream()
|
||||||
@@ -460,6 +470,18 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertMetaDataQuota(final Long dsId, final int requested) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(dsId, requested, quotaManagement.getMaxMetaDataEntriesPerDistributionSet(),
|
||||||
|
DistributionSetMetadata.class, DistributionSet.class,
|
||||||
|
distributionSetMetadataRepository::countByDistributionSetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSoftwareModuleQuota(final Long id, final int requested) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(id, requested, quotaManagement.getMaxSoftwareModulesPerDistributionSet(),
|
||||||
|
SoftwareModule.class, DistributionSet.class,
|
||||||
|
softwareModuleRepository::countByAssignedToId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
@@ -500,9 +522,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
|||||||
private JpaDistributionSet touch(final DistributionSet ds) {
|
private JpaDistributionSet touch(final DistributionSet ds) {
|
||||||
|
|
||||||
// merge base distribution set so optLockRevision gets updated and audit
|
// merge base distribution set so optLockRevision gets updated and audit
|
||||||
// log written because
|
// log written because modifying metadata is modifying the base
|
||||||
// modifying metadata is modifying the base distribution set itself for
|
// distribution set itself for auditing purposes.
|
||||||
// auditing purposes.
|
|
||||||
final JpaDistributionSet result = entityManager.merge((JpaDistributionSet) ds);
|
final JpaDistributionSet result = entityManager.merge((JpaDistributionSet) ds);
|
||||||
result.setLastModifiedAt(0L);
|
result.setLastModifiedAt(0L);
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
|
import org.eclipse.hawkbit.repository.DistributionSetTypeFields;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
|
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeCreate;
|
||||||
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
|
import org.eclipse.hawkbit.repository.builder.DistributionSetTypeUpdate;
|
||||||
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
|
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetTypeUpdate;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
|
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetTypeCreate;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||||
@@ -29,6 +31,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpecification;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||||
@@ -64,17 +67,20 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
|||||||
private final NoCountPagingRepository criteriaNoCountDao;
|
private final NoCountPagingRepository criteriaNoCountDao;
|
||||||
private final Database database;
|
private final Database database;
|
||||||
|
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
|
JpaDistributionSetTypeManagement(final DistributionSetTypeRepository distributionSetTypeRepository,
|
||||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||||
final DistributionSetRepository distributionSetRepository,
|
final DistributionSetRepository distributionSetRepository,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer, final NoCountPagingRepository criteriaNoCountDao,
|
final VirtualPropertyReplacer virtualPropertyReplacer, final NoCountPagingRepository criteriaNoCountDao,
|
||||||
final Database database) {
|
final Database database, final QuotaManagement quotaManagement) {
|
||||||
this.distributionSetTypeRepository = distributionSetTypeRepository;
|
this.distributionSetTypeRepository = distributionSetTypeRepository;
|
||||||
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
|
this.softwareModuleTypeRepository = softwareModuleTypeRepository;
|
||||||
this.distributionSetRepository = distributionSetRepository;
|
this.distributionSetRepository = distributionSetRepository;
|
||||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||||
this.criteriaNoCountDao = criteriaNoCountDao;
|
this.criteriaNoCountDao = criteriaNoCountDao;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,6 +122,7 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
|||||||
|
|
||||||
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
|
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
|
||||||
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
|
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
|
||||||
|
assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size());
|
||||||
|
|
||||||
modules.forEach(type::addMandatoryModuleType);
|
modules.forEach(type::addMandatoryModuleType);
|
||||||
|
|
||||||
@@ -138,11 +145,31 @@ public class JpaDistributionSetTypeManagement implements DistributionSetTypeMana
|
|||||||
|
|
||||||
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
|
final JpaDistributionSetType type = findDistributionSetTypeAndThrowExceptionIfNotFound(dsTypeId);
|
||||||
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
|
checkDistributionSetTypeSoftwareModuleTypesIsAllowedToModify(dsTypeId);
|
||||||
|
assertSoftwareModuleTypeQuota(dsTypeId, softwareModulesTypeIds.size());
|
||||||
|
|
||||||
modules.forEach(type::addOptionalModuleType);
|
modules.forEach(type::addOptionalModuleType);
|
||||||
|
|
||||||
return distributionSetTypeRepository.save(type);
|
return distributionSetTypeRepository.save(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces the quota specifiying the maximum number of
|
||||||
|
* {@link SoftwareModuleType}s per {@link DistributionSetType}.
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* of the distribution set type
|
||||||
|
* @param requested
|
||||||
|
* number of software module types to check
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the software module type quota is exceeded
|
||||||
|
*/
|
||||||
|
private void assertSoftwareModuleTypeQuota(final long id, final int requested) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(id, requested,
|
||||||
|
quotaManagement.getMaxSoftwareModuleTypesPerDistributionSetType(), SoftwareModuleType.class,
|
||||||
|
DistributionSetType.class, distributionSetTypeRepository::countSmTypesById);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditio
|
|||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Action;
|
import org.eclipse.hawkbit.repository.model.Action;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||||
@@ -226,11 +227,15 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
|
private Rollout createRolloutGroups(final int amountOfGroups, final RolloutGroupConditions conditions,
|
||||||
final Rollout rollout) {
|
final JpaRollout rollout) {
|
||||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
||||||
RolloutHelper.verifyRolloutGroupConditions(conditions);
|
RolloutHelper.verifyRolloutGroupConditions(conditions);
|
||||||
|
|
||||||
final JpaRollout savedRollout = (JpaRollout) rollout;
|
final JpaRollout savedRollout = rollout;
|
||||||
|
|
||||||
|
// we can enforce the 'max targets per group' quota right here because
|
||||||
|
// we want to distribute the targets equally to the different groups
|
||||||
|
assertTargetsPerRolloutGroupQuota(rollout.getTotalTargets() / amountOfGroups);
|
||||||
|
|
||||||
RolloutGroup lastSavedGroup = null;
|
RolloutGroup lastSavedGroup = null;
|
||||||
for (int i = 0; i < amountOfGroups; i++) {
|
for (int i = 0; i < amountOfGroups; i++) {
|
||||||
@@ -269,7 +274,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
||||||
final JpaRollout savedRollout = (JpaRollout) rollout;
|
final JpaRollout savedRollout = (JpaRollout) rollout;
|
||||||
|
|
||||||
// Preparing the groups
|
// prepare the groups
|
||||||
final List<RolloutGroup> groups = groupList.stream()
|
final List<RolloutGroup> groups = groupList.stream()
|
||||||
.map(group -> JpaRolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
|
.map(group -> JpaRolloutHelper.prepareRolloutGroupWithDefaultConditions(group, conditions))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -278,7 +283,13 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
RolloutHelper.verifyRemainingTargets(
|
RolloutHelper.verifyRemainingTargets(
|
||||||
calculateRemainingTargets(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt()));
|
calculateRemainingTargets(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt()));
|
||||||
|
|
||||||
// Persisting the groups
|
// check if we need to enforce the 'max targets per group' quota
|
||||||
|
if (quotaManagement.getMaxTargetsPerRolloutGroup() > 0) {
|
||||||
|
validateTargetsInGroups(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt())
|
||||||
|
.getTargetsPerGroup().forEach(this::assertTargetsPerRolloutGroupQuota);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and persist the groups (w/o filling them with targets)
|
||||||
RolloutGroup lastSavedGroup = null;
|
RolloutGroup lastSavedGroup = null;
|
||||||
for (final RolloutGroup srcGroup : groups) {
|
for (final RolloutGroup srcGroup : groups) {
|
||||||
final JpaRolloutGroup group = new JpaRolloutGroup();
|
final JpaRolloutGroup group = new JpaRolloutGroup();
|
||||||
@@ -346,7 +357,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
// When all groups are ready the rollout status can be changed to be
|
// When all groups are ready the rollout status can be changed to be
|
||||||
// ready, too.
|
// ready, too.
|
||||||
if (readyGroups == rolloutGroups.size()) {
|
if (readyGroups == rolloutGroups.size()) {
|
||||||
LOGGER.debug("rollout {} creatin done. Switch to READY.", rollout.getId());
|
LOGGER.debug("rollout {} creation done. Switch to READY.", rollout.getId());
|
||||||
rollout.setStatus(RolloutStatus.READY);
|
rollout.setStatus(RolloutStatus.READY);
|
||||||
rollout.setLastCheck(0);
|
rollout.setLastCheck(0);
|
||||||
rollout.setTotalTargets(totalTargets);
|
rollout.setTotalTargets(totalTargets);
|
||||||
@@ -383,9 +394,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
return rolloutGroupRepository.save(group);
|
return rolloutGroupRepository.save(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
long targetsLeftToAdd = expectedInGroup - currentlyInGroup;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
long targetsLeftToAdd = expectedInGroup - currentlyInGroup;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Add up to TRANSACTION_TARGETS of the left targets
|
// Add up to TRANSACTION_TARGETS of the left targets
|
||||||
// In case a TransactionException is thrown this loop aborts
|
// In case a TransactionException is thrown this loop aborts
|
||||||
@@ -558,6 +570,9 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||||
actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED);
|
actionRepository.switchStatus(Action.Status.CANCELED, targetIds, false, Action.Status.SCHEDULED);
|
||||||
targets.forEach(target -> {
|
targets.forEach(target -> {
|
||||||
|
|
||||||
|
assertActionsPerTargetQuota(target, 1);
|
||||||
|
|
||||||
final JpaAction action = new JpaAction();
|
final JpaAction action = new JpaAction();
|
||||||
action.setTarget(target);
|
action.setTarget(target);
|
||||||
action.setActive(false);
|
action.setActive(false);
|
||||||
@@ -1006,6 +1021,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
return rollout;
|
return rollout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean exists(final long rolloutId) {
|
||||||
|
return rolloutRepository.exists(rolloutId);
|
||||||
|
}
|
||||||
|
|
||||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rollouts) {
|
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRollout(final List<Long> rollouts) {
|
||||||
if (rollouts.isEmpty()) {
|
if (rollouts.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -1044,8 +1064,33 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean exists(final long rolloutId) {
|
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||||
return rolloutRepository.exists(rolloutId);
|
* {@link RolloutGroup}.
|
||||||
|
*
|
||||||
|
* @param group
|
||||||
|
* The rollout group
|
||||||
|
* @param requested
|
||||||
|
* number of targets to check
|
||||||
|
*/
|
||||||
|
private void assertTargetsPerRolloutGroupQuota(final long requested) {
|
||||||
|
final int quota = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
QuotaHelper.assertAssignmentQuota(requested, quota, Target.class, RolloutGroup.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforces the quota defining the maximum number of {@link Action}s per
|
||||||
|
* {@link Target}.
|
||||||
|
*
|
||||||
|
* @param target
|
||||||
|
* The target
|
||||||
|
* @param requested
|
||||||
|
* number of actions to check
|
||||||
|
*/
|
||||||
|
private void assertActionsPerTargetQuota(final Target target, final int requested) {
|
||||||
|
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||||
|
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class,
|
||||||
|
actionRepository::countByTargetId);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -28,6 +29,7 @@ import javax.persistence.criteria.Root;
|
|||||||
|
|
||||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
import org.eclipse.hawkbit.repository.SoftwareModuleFields;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||||
@@ -53,6 +55,7 @@ import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SoftwareModuleSpecification;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
import org.eclipse.hawkbit.repository.model.AssignedSoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
@@ -103,7 +106,10 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
|
|
||||||
private final ArtifactManagement artifactManagement;
|
private final ArtifactManagement artifactManagement;
|
||||||
|
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||||
|
|
||||||
private final Database database;
|
private final Database database;
|
||||||
|
|
||||||
JpaSoftwareModuleManagement(final EntityManager entityManager,
|
JpaSoftwareModuleManagement(final EntityManager entityManager,
|
||||||
@@ -112,8 +118,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||||
final NoCountPagingRepository criteriaNoCountDao, final AuditorAware<String> auditorProvider,
|
final NoCountPagingRepository criteriaNoCountDao, final AuditorAware<String> auditorProvider,
|
||||||
final ArtifactManagement artifactManagement, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement,
|
||||||
final Database database) {
|
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
this.distributionSetRepository = distributionSetRepository;
|
this.distributionSetRepository = distributionSetRepository;
|
||||||
this.softwareModuleRepository = softwareModuleRepository;
|
this.softwareModuleRepository = softwareModuleRepository;
|
||||||
@@ -122,6 +128,7 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
this.criteriaNoCountDao = criteriaNoCountDao;
|
this.criteriaNoCountDao = criteriaNoCountDao;
|
||||||
this.auditorProvider = auditorProvider;
|
this.auditorProvider = auditorProvider;
|
||||||
this.artifactManagement = artifactManagement;
|
this.artifactManagement = artifactManagement;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
@@ -461,19 +468,13 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
@Retryable(include = {
|
@Retryable(include = {
|
||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) {
|
public SoftwareModuleMetadata createMetaData(final SoftwareModuleMetadataCreate c) {
|
||||||
|
|
||||||
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
|
final JpaSoftwareModuleMetadataCreate create = (JpaSoftwareModuleMetadataCreate) c;
|
||||||
|
final Long moduleId = create.getSoftwareModuleId();
|
||||||
|
assertSoftwareModuleExists(moduleId);
|
||||||
|
assertMetaDataQuota(moduleId, 1);
|
||||||
|
|
||||||
checkAndThrowAlreadyIfSoftwareModuleMetadataExists(create.getSoftwareModuleId(), create);
|
return saveMetadata(create);
|
||||||
touch(create.getSoftwareModuleId());
|
|
||||||
|
|
||||||
return softwareModuleMetadataRepository.save(create.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAndThrowAlreadyIfSoftwareModuleMetadataExists(final Long moduleId,
|
|
||||||
final JpaSoftwareModuleMetadataCreate md) {
|
|
||||||
if (softwareModuleMetadataRepository.exists(new SwMetadataCompositeKey(moduleId, md.getKey()))) {
|
|
||||||
throwMetadataKeyAlreadyExists(md.getKey());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -482,7 +483,71 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||||
public List<SoftwareModuleMetadata> createMetaData(final Collection<SoftwareModuleMetadataCreate> create) {
|
public List<SoftwareModuleMetadata> createMetaData(final Collection<SoftwareModuleMetadataCreate> create) {
|
||||||
|
|
||||||
return create.stream().map(this::createMetaData).collect(Collectors.toList());
|
if (!create.isEmpty()) {
|
||||||
|
|
||||||
|
// check if all meta data entries refer to the same software module
|
||||||
|
final Long moduleId = ((JpaSoftwareModuleMetadataCreate) create.iterator().next()).getSoftwareModuleId();
|
||||||
|
if (createJpaMetadataCreateStream(create).allMatch(c -> moduleId.equals(c.getSoftwareModuleId()))) {
|
||||||
|
|
||||||
|
assertSoftwareModuleExists(moduleId);
|
||||||
|
assertMetaDataQuota(moduleId, create.size());
|
||||||
|
|
||||||
|
return createJpaMetadataCreateStream(create).map(this::saveMetadata).collect(Collectors.toList());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// group by software module id to minimize database access
|
||||||
|
final Map<Long, List<JpaSoftwareModuleMetadataCreate>> groups = createJpaMetadataCreateStream(create)
|
||||||
|
.collect(Collectors.groupingBy(JpaSoftwareModuleMetadataCreate::getSoftwareModuleId));
|
||||||
|
return groups.entrySet().stream().flatMap(e -> {
|
||||||
|
|
||||||
|
final Long id = e.getKey();
|
||||||
|
final List<JpaSoftwareModuleMetadataCreate> group = e.getValue();
|
||||||
|
|
||||||
|
assertSoftwareModuleExists(id);
|
||||||
|
assertMetaDataQuota(id, group.size());
|
||||||
|
|
||||||
|
return group.stream().map(this::saveMetadata);
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<JpaSoftwareModuleMetadataCreate> createJpaMetadataCreateStream(
|
||||||
|
final Collection<SoftwareModuleMetadataCreate> create) {
|
||||||
|
return create.stream().map(c -> (JpaSoftwareModuleMetadataCreate) c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SoftwareModuleMetadata saveMetadata(final JpaSoftwareModuleMetadataCreate create) {
|
||||||
|
assertSoftwareModuleMetadataDoesNotExist(create.getSoftwareModuleId(), create);
|
||||||
|
return softwareModuleMetadataRepository.save(create.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSoftwareModuleMetadataDoesNotExist(final Long moduleId,
|
||||||
|
final JpaSoftwareModuleMetadataCreate md) {
|
||||||
|
if (softwareModuleMetadataRepository.exists(new SwMetadataCompositeKey(moduleId, md.getKey()))) {
|
||||||
|
throwMetadataKeyAlreadyExists(md.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertSoftwareModuleExists(final Long moduleId) {
|
||||||
|
touch(moduleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the meta data quota for the software module with the given ID.
|
||||||
|
*
|
||||||
|
* @param moduleId
|
||||||
|
* The software module ID.
|
||||||
|
* @param requested
|
||||||
|
* Number of meta data entries to be created.
|
||||||
|
*/
|
||||||
|
private void assertMetaDataQuota(final Long moduleId, final int requested) {
|
||||||
|
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||||
|
QuotaHelper.assertAssignmentQuota(moduleId, requested, maxMetaData, SoftwareModuleMetadata.class,
|
||||||
|
SoftwareModule.class, softwareModuleMetadataRepository::countBySoftwareModuleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -514,9 +579,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
|
|||||||
*/
|
*/
|
||||||
private JpaSoftwareModule touch(final SoftwareModule latestModule) {
|
private JpaSoftwareModule touch(final SoftwareModule latestModule) {
|
||||||
// merge base distribution set so optLockRevision gets updated and audit
|
// merge base distribution set so optLockRevision gets updated and audit
|
||||||
// log written because
|
// log written because modifying metadata is modifying the base
|
||||||
// modifying metadata is modifying the base distribution set itself for
|
// distribution set itself for auditing purposes.
|
||||||
// auditing purposes.
|
|
||||||
final JpaSoftwareModule result = entityManager.merge((JpaSoftwareModule) latestModule);
|
final JpaSoftwareModule result = entityManager.merge((JpaSoftwareModule) latestModule);
|
||||||
result.setLastModifiedAt(0L);
|
result.setLastModifiedAt(0L);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.List;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetFields;
|
import org.eclipse.hawkbit.repository.TargetFields;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||||
@@ -28,7 +29,9 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||||
import org.springframework.dao.ConcurrencyFailureException;
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
@@ -56,18 +59,24 @@ import com.google.common.collect.Lists;
|
|||||||
public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
public class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||||
|
|
||||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||||
|
private final TargetRepository targetRepository;
|
||||||
|
|
||||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||||
|
|
||||||
private final DistributionSetManagement distributionSetManagement;
|
private final DistributionSetManagement distributionSetManagement;
|
||||||
|
private final QuotaManagement quotaManagement;
|
||||||
|
|
||||||
private final Database database;
|
private final Database database;
|
||||||
|
|
||||||
JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
|
JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
final TargetRepository targetRepository, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final DistributionSetManagement distributionSetManagement, final Database database) {
|
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
|
||||||
|
final Database database) {
|
||||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||||
|
this.targetRepository = targetRepository;
|
||||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||||
this.distributionSetManagement = distributionSetManagement;
|
this.distributionSetManagement = distributionSetManagement;
|
||||||
|
this.quotaManagement = quotaManagement;
|
||||||
this.database = database;
|
this.database = database;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +87,10 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
|||||||
public TargetFilterQuery create(final TargetFilterQueryCreate c) {
|
public TargetFilterQuery create(final TargetFilterQueryCreate c) {
|
||||||
final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c;
|
final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c;
|
||||||
|
|
||||||
|
// enforce the 'max targets per auto assign' quota right here even if
|
||||||
|
// the result of the filter query can vary over time
|
||||||
|
create.getSet().flatMap(set -> create.getQuery()).ifPresent(this::assertMaxTargetsQuota);
|
||||||
|
|
||||||
return targetFilterQueryRepository.save(create.build());
|
return targetFilterQueryRepository.save(create.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +200,18 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
|||||||
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.getId());
|
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.getId());
|
||||||
|
|
||||||
update.getName().ifPresent(targetFilterQuery::setName);
|
update.getName().ifPresent(targetFilterQuery::setName);
|
||||||
update.getQuery().ifPresent(targetFilterQuery::setQuery);
|
update.getQuery().ifPresent(query -> {
|
||||||
|
|
||||||
|
// enforce the 'max targets per auto assignment'-quota only if the
|
||||||
|
// query is going to change
|
||||||
|
if (targetFilterQuery.getAutoAssignDistributionSet() != null
|
||||||
|
&& !query.equals(targetFilterQuery.getQuery())) {
|
||||||
|
assertMaxTargetsQuota(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the new query
|
||||||
|
targetFilterQuery.setQuery(query);
|
||||||
|
});
|
||||||
|
|
||||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||||
}
|
}
|
||||||
@@ -200,6 +224,13 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
|||||||
targetFilterQuery.setAutoAssignDistributionSet(
|
targetFilterQuery.setAutoAssignDistributionSet(
|
||||||
Optional.ofNullable(dsId).map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null));
|
Optional.ofNullable(dsId).map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null));
|
||||||
|
|
||||||
|
// we cannot be sure that the quota was enforced at creation time
|
||||||
|
// because the Target Filter Query REST API does not allow to specify an
|
||||||
|
// auto-assign distribution set when creating a target filter query
|
||||||
|
if (dsId != null) {
|
||||||
|
assertMaxTargetsQuota(targetFilterQuery.getQuery());
|
||||||
|
}
|
||||||
|
|
||||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,4 +250,10 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertMaxTargetsQuota(final String query) {
|
||||||
|
QuotaHelper.assertAssignmentQuota(
|
||||||
|
targetRepository.count(RSQLUtility.parse(query, TargetFields.class, virtualPropertyReplacer, database)),
|
||||||
|
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,12 +93,23 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
|
|||||||
*
|
*
|
||||||
* @param pageReq
|
* @param pageReq
|
||||||
* Pageable
|
* Pageable
|
||||||
* @param swId
|
* @param softwareModuleId
|
||||||
* software module id
|
* software module id
|
||||||
*
|
*
|
||||||
* @return Page<Artifact>
|
* @return Page<Artifact>
|
||||||
*/
|
*/
|
||||||
Page<Artifact> findBySoftwareModuleId(Pageable pageReq, final Long swId);
|
Page<Artifact> findBySoftwareModuleId(Pageable pageReq, Long softwareModuleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the artifacts that are associated with the given software module.
|
||||||
|
*
|
||||||
|
* @param softwareModuleId
|
||||||
|
* software module ID
|
||||||
|
*
|
||||||
|
* @return the current number of artifacts associated with the software
|
||||||
|
* module.
|
||||||
|
*/
|
||||||
|
long countBySoftwareModuleId(Long softwareModuleId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Searches for a {@link Artifact} based user provided filename at upload
|
* Searches for a {@link Artifact} based user provided filename at upload
|
||||||
@@ -108,8 +119,9 @@ public interface LocalArtifactRepository extends BaseEntityRepository<JpaArtifac
|
|||||||
* to search
|
* to search
|
||||||
* @param softwareModuleId
|
* @param softwareModuleId
|
||||||
* selected software module id
|
* selected software module id
|
||||||
|
*
|
||||||
* @return list of {@link Artifact}.
|
* @return list of {@link Artifact}.
|
||||||
*/
|
*/
|
||||||
Optional<Artifact> findFirstByFilenameAndSoftwareModuleId(final String filename, final Long softwareModuleId);
|
Optional<Artifact> findFirstByFilenameAndSoftwareModuleId(String filename, Long softwareModuleId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
@@ -43,9 +44,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
|
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||||
final ActionStatusRepository actionStatusRepository) {
|
final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) {
|
||||||
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
||||||
actionStatusRepository);
|
actionStatusRepository, quotaManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
@@ -41,9 +42,9 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
|||||||
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
|
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||||
final ActionStatusRepository actionStatusRepository) {
|
final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) {
|
||||||
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
||||||
actionStatusRepository);
|
actionStatusRepository, quotaManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
|||||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||||
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
|
import org.eclipse.hawkbit.repository.PropertiesQuotaManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration;
|
import org.eclipse.hawkbit.repository.RepositoryDefaultConfiguration;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||||
@@ -69,6 +70,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
|
|||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
import org.eclipse.hawkbit.repository.model.helper.SystemManagementHolder;
|
||||||
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
import org.eclipse.hawkbit.repository.model.helper.TenantConfigurationManagementHolder;
|
||||||
@@ -376,7 +378,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
DistributionSetManagement distributionSetManagement(final EntityManager entityManager,
|
DistributionSetManagement distributionSetManagement(final EntityManager entityManager,
|
||||||
final DistributionSetRepository distributionSetRepository,
|
final DistributionSetRepository distributionSetRepository,
|
||||||
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
|
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
|
||||||
final DistributionSetTypeManagement distributionSetTypeManagement,
|
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
|
||||||
final DistributionSetMetadataRepository distributionSetMetadataRepository,
|
final DistributionSetMetadataRepository distributionSetMetadataRepository,
|
||||||
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
|
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
|
||||||
final NoCountPagingRepository criteriaNoCountDao, final ApplicationEventPublisher eventPublisher,
|
final NoCountPagingRepository criteriaNoCountDao, final ApplicationEventPublisher eventPublisher,
|
||||||
@@ -386,7 +388,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
final DistributionSetTagRepository distributionSetTagRepository,
|
final DistributionSetTagRepository distributionSetTagRepository,
|
||||||
final AfterTransactionCommitExecutor afterCommit, final JpaProperties properties) {
|
final AfterTransactionCommitExecutor afterCommit, final JpaProperties properties) {
|
||||||
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
|
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
|
||||||
systemManagement, distributionSetTypeManagement, distributionSetMetadataRepository,
|
systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository,
|
||||||
targetFilterQueryRepository, actionRepository, criteriaNoCountDao, eventPublisher, applicationContext,
|
targetFilterQueryRepository, actionRepository, criteriaNoCountDao, eventPublisher, applicationContext,
|
||||||
tenantAware, virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository,
|
tenantAware, virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository,
|
||||||
afterCommit, properties.getDatabase());
|
afterCommit, properties.getDatabase());
|
||||||
@@ -405,9 +407,10 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||||
final DistributionSetRepository distributionSetRepository,
|
final DistributionSetRepository distributionSetRepository,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer, final NoCountPagingRepository criteriaNoCountDao,
|
final VirtualPropertyReplacer virtualPropertyReplacer, final NoCountPagingRepository criteriaNoCountDao,
|
||||||
final JpaProperties properties) {
|
final JpaProperties properties, final QuotaManagement quotaManagement) {
|
||||||
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
|
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
|
||||||
distributionSetRepository, virtualPropertyReplacer, criteriaNoCountDao, properties.getDatabase());
|
distributionSetRepository, virtualPropertyReplacer, criteriaNoCountDao, properties.getDatabase(),
|
||||||
|
quotaManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,22 +460,29 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
* {@link JpaTargetFilterQueryManagement} bean.
|
* {@link JpaTargetFilterQueryManagement} bean.
|
||||||
*
|
*
|
||||||
* @param targetFilterQueryRepository
|
* @param targetFilterQueryRepository
|
||||||
* to query entity access
|
* holding {@link TargetFilterQuery} entities
|
||||||
|
* @param targetRepository
|
||||||
|
* holding {@link Target} entities
|
||||||
* @param virtualPropertyReplacer
|
* @param virtualPropertyReplacer
|
||||||
* for RSQL handling
|
* for RSQL handling
|
||||||
* @param distributionSetManagement
|
* @param distributionSetManagement
|
||||||
* for auto assign DS access
|
* for auto assign DS access
|
||||||
|
* @param quotaManagement
|
||||||
|
* to access quotas
|
||||||
|
* @param properties
|
||||||
|
* JPA properties
|
||||||
*
|
*
|
||||||
* @return a new {@link TargetFilterQueryManagement}
|
* @return a new {@link TargetFilterQueryManagement}
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
TargetFilterQueryManagement targetFilterQueryManagement(
|
TargetFilterQueryManagement targetFilterQueryManagement(
|
||||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetRepository targetRepository,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer,
|
final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final DistributionSetManagement distributionSetManagement, final JpaProperties properties) {
|
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
|
||||||
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, virtualPropertyReplacer,
|
final JpaProperties properties) {
|
||||||
distributionSetManagement, properties.getDatabase());
|
return new JpaTargetFilterQueryManagement(targetFilterQueryRepository, targetRepository,
|
||||||
|
virtualPropertyReplacer, distributionSetManagement, quotaManagement, properties.getDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -518,11 +528,11 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
final SoftwareModuleMetadataRepository softwareModuleMetadataRepository,
|
||||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||||
final NoCountPagingRepository criteriaNoCountDao, final AuditorAware<String> auditorProvider,
|
final NoCountPagingRepository criteriaNoCountDao, final AuditorAware<String> auditorProvider,
|
||||||
final ArtifactManagement artifactManagement, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final ArtifactManagement artifactManagement, final QuotaManagement quotaManagement,
|
||||||
final JpaProperties properties) {
|
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
|
||||||
return new JpaSoftwareModuleManagement(entityManager, distributionSetRepository, softwareModuleRepository,
|
return new JpaSoftwareModuleManagement(entityManager, distributionSetRepository, softwareModuleRepository,
|
||||||
softwareModuleMetadataRepository, softwareModuleTypeRepository, criteriaNoCountDao, auditorProvider,
|
softwareModuleMetadataRepository, softwareModuleTypeRepository, criteriaNoCountDao, auditorProvider,
|
||||||
artifactManagement, virtualPropertyReplacer, properties.getDatabase());
|
artifactManagement, quotaManagement, virtualPropertyReplacer, properties.getDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -585,12 +595,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext,
|
final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext,
|
||||||
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
|
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||||
final PlatformTransactionManager txManager,
|
final PlatformTransactionManager txManager,
|
||||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||||
final SystemSecurityContext systemSecurityContext, final JpaProperties properties) {
|
final SystemSecurityContext systemSecurityContext, final JpaProperties properties) {
|
||||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
|
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
|
||||||
actionStatusRepository, targetManagement, auditorProvider, eventPublisher, applicationContext,
|
actionStatusRepository, targetManagement, auditorProvider, eventPublisher, applicationContext,
|
||||||
afterCommit, virtualPropertyReplacer, txManager, tenantConfigurationManagement, systemSecurityContext,
|
afterCommit, virtualPropertyReplacer, txManager, tenantConfigurationManagement, quotaManagement,
|
||||||
properties.getDatabase());
|
systemSecurityContext, properties.getDatabase());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -609,9 +619,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
|||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
ArtifactManagement artifactManagement(final LocalArtifactRepository localArtifactRepository,
|
ArtifactManagement artifactManagement(final LocalArtifactRepository localArtifactRepository,
|
||||||
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
|
final SoftwareModuleRepository softwareModuleRepository, final ArtifactRepository artifactRepository,
|
||||||
final TenantAware tenantAware) {
|
final QuotaManagement quotaManagement, final TenantAware tenantAware) {
|
||||||
return new JpaArtifactManagement(localArtifactRepository, softwareModuleRepository, artifactRepository,
|
return new JpaArtifactManagement(localArtifactRepository, softwareModuleRepository, artifactRepository,
|
||||||
tenantAware);
|
quotaManagement, tenantAware);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,11 +30,49 @@ public interface SoftwareModuleMetadataRepository
|
|||||||
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
extends PagingAndSortingRepository<JpaSoftwareModuleMetadata, SwMetadataCompositeKey>,
|
||||||
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
JpaSpecificationExecutor<JpaSoftwareModuleMetadata> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the meta data entries that match the given software module ID and
|
||||||
|
* target visibility flag.
|
||||||
|
*
|
||||||
|
* @param page
|
||||||
|
* The pagination parameters.
|
||||||
|
* @param moduleId
|
||||||
|
* The ID of the software module.
|
||||||
|
* @param targetVisible
|
||||||
|
* The target visibility flag.
|
||||||
|
*
|
||||||
|
* @return A {@link Page} with the matching meta data entries.
|
||||||
|
*/
|
||||||
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(Pageable page, Long moduleId,
|
Page<JpaSoftwareModuleMetadata> findBySoftwareModuleIdAndTargetVisible(Pageable page, Long moduleId,
|
||||||
boolean targetVisible);
|
boolean targetVisible);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locates the meta data entries that match the given software module IDs
|
||||||
|
* and target visibility flag.
|
||||||
|
*
|
||||||
|
* @param page
|
||||||
|
* The pagination parameters.
|
||||||
|
* @param moduleId
|
||||||
|
* List of software module IDs.
|
||||||
|
* @param targetVisible
|
||||||
|
* The target visibility flag.
|
||||||
|
*
|
||||||
|
* @return A {@link Page} with the matching meta data entries.
|
||||||
|
*/
|
||||||
@Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
|
@Query("SELECT smd.softwareModule.id, smd FROM JpaSoftwareModuleMetadata smd WHERE smd.softwareModule.id IN :moduleId AND smd.targetVisible = :targetVisible")
|
||||||
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(Pageable page, @Param("moduleId") Collection<Long> moduleId,
|
Page<Object[]> findBySoftwareModuleIdInAndTargetVisible(Pageable page, @Param("moduleId") Collection<Long> moduleId,
|
||||||
@Param("targetVisible") boolean targetVisible);
|
@Param("targetVisible") boolean targetVisible);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the meta data entries that are associated with the addressed
|
||||||
|
* software module.
|
||||||
|
*
|
||||||
|
* @param moduleId
|
||||||
|
* The ID of the software module.
|
||||||
|
*
|
||||||
|
* @return The number of meta data entries associated with the software
|
||||||
|
* module.
|
||||||
|
*/
|
||||||
|
long countBySoftwareModuleId(@Param("moduleId") Long moduleId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ public interface SoftwareModuleRepository
|
|||||||
*/
|
*/
|
||||||
Page<SoftwareModule> findByAssignedToId(Pageable pageable, Long setId);
|
Page<SoftwareModule> findByAssignedToId(Pageable pageable, Long setId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the software modules which are assigned to the distribution set
|
||||||
|
* with the given ID.
|
||||||
|
*
|
||||||
|
* @param setId
|
||||||
|
* the distribution set ID
|
||||||
|
*
|
||||||
|
* @return the number of software modules matching the given distribution
|
||||||
|
* set ID.
|
||||||
|
*/
|
||||||
|
long countByAssignedToId(Long setId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pageable
|
* @param pageable
|
||||||
* the page request to page the result set
|
* the page request to page the result set
|
||||||
|
|||||||
@@ -65,9 +65,10 @@ public class AutoAssignScheduler {
|
|||||||
LOGGER.debug("auto assign schedule checker has been triggered.");
|
LOGGER.debug("auto assign schedule checker has been triggered.");
|
||||||
// run this code in system code privileged to have the necessary
|
// run this code in system code privileged to have the necessary
|
||||||
// permission to query and create entities.
|
// permission to query and create entities.
|
||||||
systemSecurityContext.runAsSystem(() -> executeAutoAssign());
|
systemSecurityContext.runAsSystem(this::executeAutoAssign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("squid:S3516")
|
||||||
private Object executeAutoAssign() {
|
private Object executeAutoAssign() {
|
||||||
// workaround eclipselink that is currently not possible to
|
// workaround eclipselink that is currently not possible to
|
||||||
// execute a query without multitenancy if MultiTenant
|
// execute a query without multitenancy if MultiTenant
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ public final class RSQLUtility {
|
|||||||
|
|
||||||
private final SimpleTypeConverter simpleTypeConverter;
|
private final SimpleTypeConverter simpleTypeConverter;
|
||||||
|
|
||||||
final Database database;
|
private final Database database;
|
||||||
|
|
||||||
private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType,
|
private JpqQueryRSQLVisitor(final Root<T> root, final CriteriaBuilder cb, final Class<A> enumType,
|
||||||
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
|
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) {
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* 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.repository.jpa.utils;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to check assignment quotas.
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
public final class QuotaHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class logger
|
||||||
|
*/
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(QuotaHelper.class);
|
||||||
|
|
||||||
|
private QuotaHelper() {
|
||||||
|
// no need to instantiate this class
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the specified assignment quota.
|
||||||
|
*
|
||||||
|
* @param requested
|
||||||
|
* The number of entities that shall be assigned to the parent
|
||||||
|
* entity.
|
||||||
|
* @param limit
|
||||||
|
* The maximum number of entities that may be assigned to the
|
||||||
|
* parent entity.
|
||||||
|
* @param type
|
||||||
|
* The type of the entities that shall be assigned.
|
||||||
|
* @param parentType
|
||||||
|
* The type of the parent entity.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the assignment operation would cause the quota to be
|
||||||
|
* exceeded
|
||||||
|
*/
|
||||||
|
public static void assertAssignmentQuota(final long requested, final long limit, @NotNull final Class<?> type,
|
||||||
|
@NotNull final Class<?> parentType) {
|
||||||
|
assertAssignmentQuota(null, requested, limit, type.getSimpleName(), parentType.getSimpleName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the specified assignment quota.
|
||||||
|
*
|
||||||
|
* @param parentId
|
||||||
|
* The ID of the parent entity.
|
||||||
|
* @param requested
|
||||||
|
* The number of entities that shall be assigned to the parent
|
||||||
|
* entity.
|
||||||
|
* @param limit
|
||||||
|
* The maximum number of entities that may be assigned to the
|
||||||
|
* parent entity.
|
||||||
|
* @param type
|
||||||
|
* The type of the entities that shall be assigned.
|
||||||
|
* @param parentType
|
||||||
|
* The type of the parent entity.
|
||||||
|
* @param countFct
|
||||||
|
* Function to count the entities that are currently assigned to
|
||||||
|
* the parent entity.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the assignment operation would cause the quota to be
|
||||||
|
* exceeded
|
||||||
|
*/
|
||||||
|
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
|
||||||
|
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final Function<Long, Long> countFct) {
|
||||||
|
assertAssignmentQuota(parentId, requested, limit, type.getSimpleName(), parentType.getSimpleName(), countFct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the specified assignment quota.
|
||||||
|
*
|
||||||
|
* @param parentId
|
||||||
|
* The ID of the parent entity.
|
||||||
|
* @param requested
|
||||||
|
* The number of entities that shall be assigned to the parent
|
||||||
|
* entity.
|
||||||
|
* @param limit
|
||||||
|
* The maximum number of entities that may be assigned to the
|
||||||
|
* parent entity.
|
||||||
|
* @param type
|
||||||
|
* The type of the entities that shall be assigned.
|
||||||
|
* @param parentType
|
||||||
|
* The type of the parent entity.
|
||||||
|
* @param countFct
|
||||||
|
* Function to count the entities that are currently assigned to
|
||||||
|
* the parent entity.
|
||||||
|
*
|
||||||
|
* @throws QuotaExceededException
|
||||||
|
* if the assignment operation would cause the quota to be
|
||||||
|
* exceeded
|
||||||
|
*/
|
||||||
|
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
|
||||||
|
@NotNull final String type, @NotNull final String parentType, final Function<Long, Long> countFct) {
|
||||||
|
|
||||||
|
// check if the quota is unlimited
|
||||||
|
if (limit <= 0) {
|
||||||
|
LOG.debug("Quota 'Max {} entities per {}' is unlimited.", type, parentType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requested > limit) {
|
||||||
|
final String parentIdStr = parentId != null ? String.valueOf(parentId) : "<new>";
|
||||||
|
LOG.warn("Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}.", requested,
|
||||||
|
type, parentType, parentIdStr, limit);
|
||||||
|
throw new QuotaExceededException(type, parentType, parentId, requested, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentId != null && countFct != null) {
|
||||||
|
final long currentCount = countFct.apply(parentId);
|
||||||
|
if (currentCount + requested > limit) {
|
||||||
|
LOG.warn(
|
||||||
|
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. Currently, there are {} {} entities assigned.",
|
||||||
|
requested, type, parentType, parentId, limit, currentCount, type);
|
||||||
|
throw new QuotaExceededException(type, parentType, parentId, requested, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ import java.io.IOException;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
@@ -25,6 +27,7 @@ import org.eclipse.hawkbit.repository.ArtifactManagement;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
@@ -35,6 +38,8 @@ import org.eclipse.hawkbit.repository.test.util.HashGeneratorUtils;
|
|||||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
@@ -67,12 +72,13 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@ExpectEvents({ @Expect(type = SoftwareModuleDeletedEvent.class, count = 0) })
|
@ExpectEvents({ @Expect(type = SoftwareModuleDeletedEvent.class, count = 0) })
|
||||||
public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException {
|
public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException {
|
||||||
|
|
||||||
verifyThrownExceptionBy(() -> artifactManagement.create(IOUtils.toInputStream("test", "UTF-8"), NOT_EXIST_IDL,
|
final String artifactData = "test";
|
||||||
"xxx", null, null, false, null), "SoftwareModule");
|
final int artifactSize = artifactData.length();
|
||||||
|
verifyThrownExceptionBy(() -> artifactManagement.create(IOUtils.toInputStream(artifactData, "UTF-8"),
|
||||||
|
NOT_EXIST_IDL, "xxx", null, null, false, null, artifactSize), "SoftwareModule");
|
||||||
|
|
||||||
verifyThrownExceptionBy(
|
verifyThrownExceptionBy(() -> artifactManagement.create(IOUtils.toInputStream(artifactData, "UTF-8"), 1234L,
|
||||||
() -> artifactManagement.create(IOUtils.toInputStream("test", "UTF-8"), 1234L, "xxx", false),
|
"xxx", false, artifactSize), "SoftwareModule");
|
||||||
"SoftwareModule");
|
|
||||||
|
|
||||||
verifyThrownExceptionBy(() -> artifactManagement.delete(NOT_EXIST_IDL), "Artifact");
|
verifyThrownExceptionBy(() -> artifactManagement.delete(NOT_EXIST_IDL), "Artifact");
|
||||||
|
|
||||||
@@ -86,59 +92,128 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Test if a local artifact can be created by API including metadata.")
|
@Description("Test if a local artifact can be created by API including metadata.")
|
||||||
public void createArtifact() throws NoSuchAlgorithmException, IOException {
|
public void createArtifact() throws NoSuchAlgorithmException, IOException {
|
||||||
// checkbaseline
|
|
||||||
|
// check baseline
|
||||||
assertThat(softwareModuleRepository.findAll()).hasSize(0);
|
assertThat(softwareModuleRepository.findAll()).hasSize(0);
|
||||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||||
|
|
||||||
JpaSoftwareModule sm = new JpaSoftwareModule(osType, "name 1", "version 1", null, null);
|
final JpaSoftwareModule sm = softwareModuleRepository
|
||||||
sm = softwareModuleRepository.save(sm);
|
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||||
|
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||||
|
softwareModuleRepository.save(new JpaSoftwareModule(osType, "name 3", "version 3", null, null));
|
||||||
|
|
||||||
JpaSoftwareModule sm2 = new JpaSoftwareModule(osType, "name 2", "version 2", null, null);
|
final int artifactSize = 5 * 1024;
|
||||||
sm2 = softwareModuleRepository.save(sm2);
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
JpaSoftwareModule sm3 = new JpaSoftwareModule(osType, "name 3", "version 3", null, null);
|
try (final InputStream inputStream1 = new ByteArrayInputStream(random);
|
||||||
sm3 = softwareModuleRepository.save(sm3);
|
final InputStream inputStream2 = new ByteArrayInputStream(random);
|
||||||
|
final InputStream inputStream3 = new ByteArrayInputStream(random);
|
||||||
|
final InputStream inputStream4 = new ByteArrayInputStream(random);) {
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final Artifact result = artifactManagement.create(inputStream1, sm.getId(), "file1", false, artifactSize);
|
||||||
|
artifactManagement.create(inputStream2, sm.getId(), "file11", false, artifactSize);
|
||||||
|
artifactManagement.create(inputStream3, sm.getId(), "file12", false, artifactSize);
|
||||||
|
final Artifact result2 = artifactManagement.create(inputStream4, sm2.getId(), "file2", false, artifactSize);
|
||||||
|
|
||||||
final Artifact result = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
assertThat(result).isInstanceOf(Artifact.class);
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file11", false);
|
assertThat(result.getSoftwareModule().getId()).isEqualTo(sm.getId());
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file12", false);
|
assertThat(result2.getSoftwareModule().getId()).isEqualTo(sm2.getId());
|
||||||
final Artifact result2 = artifactManagement.create(new ByteArrayInputStream(random), sm2.getId(), "file2",
|
assertThat(((JpaArtifact) result).getFilename()).isEqualTo("file1");
|
||||||
false);
|
assertThat(((JpaArtifact) result).getSha1Hash()).isNotNull();
|
||||||
|
assertThat(result).isNotEqualTo(result2);
|
||||||
|
assertThat(((JpaArtifact) result).getSha1Hash()).isEqualTo(((JpaArtifact) result2).getSha1Hash());
|
||||||
|
|
||||||
assertThat(result).isInstanceOf(Artifact.class);
|
assertThat(artifactManagement.getByFilename("file1").get().getSha1Hash())
|
||||||
assertThat(result.getSoftwareModule().getId()).isEqualTo(sm.getId());
|
.isEqualTo(HashGeneratorUtils.generateSHA1(random));
|
||||||
assertThat(result2.getSoftwareModule().getId()).isEqualTo(sm2.getId());
|
assertThat(artifactManagement.getByFilename("file1").get().getMd5Hash())
|
||||||
assertThat(((JpaArtifact) result).getFilename()).isEqualTo("file1");
|
.isEqualTo(HashGeneratorUtils.generateMD5(random));
|
||||||
assertThat(((JpaArtifact) result).getSha1Hash()).isNotNull();
|
|
||||||
assertThat(result).isNotEqualTo(result2);
|
|
||||||
assertThat(((JpaArtifact) result).getSha1Hash()).isEqualTo(((JpaArtifact) result2).getSha1Hash());
|
|
||||||
|
|
||||||
assertThat(artifactManagement.getByFilename("file1").get().getSha1Hash())
|
assertThat(artifactRepository.findAll()).hasSize(4);
|
||||||
.isEqualTo(HashGeneratorUtils.generateSHA1(random));
|
assertThat(softwareModuleRepository.findAll()).hasSize(3);
|
||||||
assertThat(artifactManagement.getByFilename("file1").get().getMd5Hash())
|
|
||||||
.isEqualTo(HashGeneratorUtils.generateMD5(random));
|
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(4);
|
assertThat(softwareModuleManagement.get(sm.getId()).get().getArtifacts()).hasSize(3);
|
||||||
assertThat(softwareModuleRepository.findAll()).hasSize(3);
|
}
|
||||||
|
|
||||||
assertThat(softwareModuleManagement.get(sm.getId()).get().getArtifacts()).hasSize(3);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the quota specifying the maximum number of artifacts per software module is enforced.")
|
||||||
|
public void createArtifactsUntilQuotaIsExceeded() throws NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
|
// create a software module
|
||||||
|
final JpaSoftwareModule sm1 = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
|
||||||
|
|
||||||
|
// now create artifacts for this module until the quota is exceeded
|
||||||
|
final long maxArtifacts = quotaManagement.getMaxArtifactsPerSoftwareModule();
|
||||||
|
final List<Long> artifactIds = Lists.newArrayList();
|
||||||
|
final int artifactSize = 5 * 1024;
|
||||||
|
for (int i = 0; i < maxArtifacts; ++i) {
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(random)) {
|
||||||
|
artifactIds.add(
|
||||||
|
artifactManagement.create(inputStream, sm1.getId(), "file" + i, false, artifactSize).getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertThat(artifactRepository.findBySoftwareModuleId(PAGE, sm1.getId()).getTotalElements())
|
||||||
|
.isEqualTo(maxArtifacts);
|
||||||
|
|
||||||
|
// create one mode to trigger the quota exceeded error
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> {
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(random)) {
|
||||||
|
artifactManagement.create(inputStream, sm1.getId(), "file" + maxArtifacts, false, artifactSize);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// delete one of the artifacts
|
||||||
|
artifactManagement.delete(artifactIds.get(0));
|
||||||
|
assertThat(artifactRepository.findBySoftwareModuleId(PAGE, sm1.getId()).getTotalElements())
|
||||||
|
.isEqualTo(maxArtifacts - 1);
|
||||||
|
|
||||||
|
// now we should be able to create an artifact again
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(random)) {
|
||||||
|
artifactManagement.create(inputStream, sm1.getId(), "fileXYZ", false, artifactSize);
|
||||||
|
assertThat(artifactRepository.findBySoftwareModuleId(PAGE, sm1.getId()).getTotalElements())
|
||||||
|
.isEqualTo(maxArtifacts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that you cannot create artifacts which exceed the configured maximum size.")
|
||||||
|
public void createArtifactFailsIfTooLarge() throws NoSuchAlgorithmException, IOException {
|
||||||
|
|
||||||
|
// create a software module
|
||||||
|
final JpaSoftwareModule sm1 = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "sm1", "1.0", null, null));
|
||||||
|
|
||||||
|
// create an artifact that exceeds the configured quota
|
||||||
|
final long maxSize = quotaManagement.getMaxArtifactSize();
|
||||||
|
final int artifactSize = Math.toIntExact(maxSize) + 8;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(random)) {
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> artifactManagement.create(inputStream, sm1.getId(), "file", false, artifactSize));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests hard delete directly on repository.")
|
@Description("Tests hard delete directly on repository.")
|
||||||
public void hardDeleteSoftwareModule() throws NoSuchAlgorithmException, IOException {
|
public void hardDeleteSoftwareModule() throws NoSuchAlgorithmException, IOException {
|
||||||
JpaSoftwareModule sm = new JpaSoftwareModule(osType, "name 1", "version 1", null, null);
|
|
||||||
sm = softwareModuleRepository.save(sm);
|
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final JpaSoftwareModule sm = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||||
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
try (final InputStream inputStream = new ByteArrayInputStream(random)) {
|
||||||
|
artifactManagement.create(inputStream, sm.getId(), "file1", false, artifactSize);
|
||||||
|
assertThat(artifactRepository.findAll()).hasSize(1);
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
softwareModuleRepository.deleteAll();
|
||||||
assertThat(artifactRepository.findAll()).hasSize(1);
|
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||||
|
}
|
||||||
softwareModuleRepository.deleteAll();
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -152,97 +227,109 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Description("Tests the deletion of a local artifact including metadata.")
|
@Description("Tests the deletion of a local artifact including metadata.")
|
||||||
public void deleteArtifact() throws NoSuchAlgorithmException, IOException {
|
public void deleteArtifact() throws NoSuchAlgorithmException, IOException {
|
||||||
JpaSoftwareModule sm = new JpaSoftwareModule(osType, "name 1", "version 1", null, null);
|
|
||||||
sm = softwareModuleRepository.save(sm);
|
|
||||||
|
|
||||||
JpaSoftwareModule sm2 = new JpaSoftwareModule(osType, "name 2", "version 2", null, null);
|
final JpaSoftwareModule sm = softwareModuleRepository
|
||||||
sm2 = softwareModuleRepository.save(sm2);
|
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||||
|
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).isEmpty();
|
assertThat(artifactRepository.findAll()).isEmpty();
|
||||||
|
|
||||||
final Artifact result = artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file1",
|
final int artifactSize = 5 * 1024;
|
||||||
false);
|
try (final InputStream inputStream1 = new RandomGeneratedInputStream(artifactSize);
|
||||||
final Artifact result2 = artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), sm2.getId(),
|
final InputStream inputStream2 = new RandomGeneratedInputStream(artifactSize)) {
|
||||||
"file2", false);
|
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(2);
|
final Artifact result = artifactManagement.create(inputStream1, sm.getId(), "file1", false, artifactSize);
|
||||||
|
final Artifact result2 = artifactManagement.create(inputStream2, sm2.getId(), "file2", false, artifactSize);
|
||||||
|
|
||||||
assertThat(result.getId()).isNotNull();
|
assertThat(artifactRepository.findAll()).hasSize(2);
|
||||||
assertThat(result2.getId()).isNotNull();
|
|
||||||
assertThat(((JpaArtifact) result).getSha1Hash()).isNotEqualTo(((JpaArtifact) result2).getSha1Hash());
|
|
||||||
|
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
assertThat(result.getId()).isNotNull();
|
||||||
.isNotNull();
|
assertThat(result2.getId()).isNotNull();
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
assertThat(((JpaArtifact) result).getSha1Hash()).isNotEqualTo(((JpaArtifact) result2).getSha1Hash());
|
||||||
.isNotNull();
|
|
||||||
|
|
||||||
artifactManagement.delete(result.getId());
|
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
||||||
|
.isNotNull();
|
||||||
|
assertThat(
|
||||||
|
binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
||||||
|
.isNotNull();
|
||||||
|
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
artifactManagement.delete(result.getId());
|
||||||
.isNull();
|
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
|
||||||
.isNotNull();
|
|
||||||
|
|
||||||
artifactManagement.delete(result2.getId());
|
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
.isNull();
|
||||||
.isNull();
|
assertThat(
|
||||||
|
binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
||||||
|
.isNotNull();
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(0);
|
artifactManagement.delete(result2.getId());
|
||||||
|
assertThat(
|
||||||
|
binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result2.getSha1Hash()))
|
||||||
|
.isNull();
|
||||||
|
|
||||||
|
assertThat(artifactRepository.findAll()).hasSize(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Test the deletion of an artifact metadata where the binary is still linked to another "
|
@Description("Test the deletion of an artifact metadata where the binary is still linked to another "
|
||||||
+ "metadata element. The expected result is that the metadata is deleted but the binary kept.")
|
+ "metadata element. The expected result is that the metadata is deleted but the binary kept.")
|
||||||
public void deleteDuplicateArtifacts() throws NoSuchAlgorithmException, IOException {
|
public void deleteDuplicateArtifacts() throws NoSuchAlgorithmException, IOException {
|
||||||
JpaSoftwareModule sm = new JpaSoftwareModule(osType, "name 1", "version 1", null, null);
|
|
||||||
sm = softwareModuleRepository.save(sm);
|
|
||||||
|
|
||||||
JpaSoftwareModule sm2 = new JpaSoftwareModule(osType, "name 2", "version 2", null, null);
|
final JpaSoftwareModule sm = softwareModuleRepository
|
||||||
sm2 = softwareModuleRepository.save(sm2);
|
.save(new JpaSoftwareModule(osType, "name 1", "version 1", null, null));
|
||||||
|
final JpaSoftwareModule sm2 = softwareModuleRepository
|
||||||
|
.save(new JpaSoftwareModule(osType, "name 2", "version 2", null, null));
|
||||||
|
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
|
|
||||||
final Artifact result = artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
try (final InputStream inputStream1 = new ByteArrayInputStream(random);
|
||||||
final Artifact result2 = artifactManagement.create(new ByteArrayInputStream(random), sm2.getId(), "file2",
|
final InputStream inputStream2 = new ByteArrayInputStream(random)) {
|
||||||
false);
|
final Artifact result = artifactManagement.create(inputStream1, sm.getId(), "file1", false, artifactSize);
|
||||||
|
final Artifact result2 = artifactManagement.create(inputStream2, sm2.getId(), "file2", false, artifactSize);
|
||||||
|
|
||||||
assertThat(artifactRepository.findAll()).hasSize(2);
|
assertThat(artifactRepository.findAll()).hasSize(2);
|
||||||
assertThat(result.getId()).isNotNull();
|
assertThat(result.getId()).isNotNull();
|
||||||
assertThat(result2.getId()).isNotNull();
|
assertThat(result2.getId()).isNotNull();
|
||||||
assertThat(((JpaArtifact) result).getSha1Hash()).isEqualTo(((JpaArtifact) result2).getSha1Hash());
|
assertThat(((JpaArtifact) result).getSha1Hash()).isEqualTo(((JpaArtifact) result2).getSha1Hash());
|
||||||
|
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
||||||
.isNotNull();
|
.isNotNull();
|
||||||
artifactManagement.delete(result.getId());
|
artifactManagement.delete(result.getId());
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
||||||
.isNotNull();
|
.isNotNull();
|
||||||
|
|
||||||
artifactManagement.delete(result2.getId());
|
artifactManagement.delete(result2.getId());
|
||||||
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
assertThat(binaryArtifactRepository.getArtifactBySha1(tenantAware.getCurrentTenant(), result.getSha1Hash()))
|
||||||
.isNull();
|
.isNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Loads an local artifact based on given ID.")
|
@Description("Loads an local artifact based on given ID.")
|
||||||
public void findArtifact() throws NoSuchAlgorithmException, IOException {
|
public void findArtifact() throws NoSuchAlgorithmException, IOException {
|
||||||
final Artifact result = artifactManagement.create(new RandomGeneratedInputStream(5 * 1024),
|
final int artifactSize = 5 * 1024;
|
||||||
testdataFactory.createSoftwareModuleOs().getId(), "file1", false);
|
try (final InputStream inputStream = new RandomGeneratedInputStream(artifactSize)) {
|
||||||
|
final Artifact result = artifactManagement.create(inputStream,
|
||||||
assertThat(artifactManagement.get(result.getId()).get()).isEqualTo(result);
|
testdataFactory.createSoftwareModuleOs().getId(), "file1", false, artifactSize);
|
||||||
|
assertThat(artifactManagement.get(result.getId()).get()).isEqualTo(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Loads an artifact binary based on given ID.")
|
@Description("Loads an artifact binary based on given ID.")
|
||||||
public void loadStreamOfArtifact() throws NoSuchAlgorithmException, IOException {
|
public void loadStreamOfArtifact() throws NoSuchAlgorithmException, IOException {
|
||||||
final byte random[] = RandomStringUtils.random(5 * 1024).getBytes();
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte random[] = RandomStringUtils.random(artifactSize).getBytes();
|
||||||
final Artifact result = artifactManagement.create(new ByteArrayInputStream(random),
|
try (final InputStream input = new ByteArrayInputStream(random)) {
|
||||||
testdataFactory.createSoftwareModuleOs().getId(), "file1", false);
|
final Artifact result = artifactManagement.create(input, testdataFactory.createSoftwareModuleOs().getId(),
|
||||||
|
"file1", false, artifactSize);
|
||||||
try (InputStream fileInputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()).get()
|
try (final InputStream inputStream = artifactManagement.loadArtifactBinary(result.getSha1Hash()).get()
|
||||||
.getFileInputStream()) {
|
.getFileInputStream()) {
|
||||||
assertTrue("The stored binary matches the given binary",
|
assertTrue("The stored binary matches the given binary",
|
||||||
IOUtils.contentEquals(new ByteArrayInputStream(random), fileInputStream));
|
IOUtils.contentEquals(new ByteArrayInputStream(random), inputStream));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,27 +347,31 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Searches an artifact through the relations of a software module.")
|
@Description("Searches an artifact through the relations of a software module.")
|
||||||
public void findArtifactBySoftwareModule() {
|
public void findArtifactBySoftwareModule() throws IOException {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).isEmpty();
|
assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).isEmpty();
|
||||||
|
|
||||||
artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file1", false);
|
final int artifactSize = 5 * 1024;
|
||||||
|
try (final InputStream input = new RandomGeneratedInputStream(artifactSize)) {
|
||||||
assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).hasSize(1);
|
artifactManagement.create(input, sm.getId(), "file1", false, artifactSize);
|
||||||
|
assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).hasSize(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Searches an artifact through the relations of a software module and the filename.")
|
@Description("Searches an artifact through the relations of a software module and the filename.")
|
||||||
public void findByFilenameAndSoftwareModule() {
|
public void findByFilenameAndSoftwareModule() throws IOException {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isNotPresent();
|
assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isNotPresent();
|
||||||
|
|
||||||
artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file1", false);
|
final int artifactSize = 5 * 1024;
|
||||||
artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), sm.getId(), "file2", false);
|
try (final InputStream inputStream1 = new RandomGeneratedInputStream(artifactSize);
|
||||||
|
final InputStream inputStream2 = new RandomGeneratedInputStream(artifactSize)) {
|
||||||
assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isPresent();
|
artifactManagement.create(inputStream1, sm.getId(), "file1", false, artifactSize);
|
||||||
|
artifactManagement.create(inputStream2, sm.getId(), "file2", false, artifactSize);
|
||||||
|
assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import javax.validation.ConstraintViolationException;
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
@@ -430,7 +431,8 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
||||||
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 2) })
|
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 2) })
|
||||||
public void hasTargetArtifactAssignedIsTrueWithMultipleArtifacts() {
|
public void hasTargetArtifactAssignedIsTrueWithMultipleArtifacts() {
|
||||||
final byte[] random = RandomUtils.nextBytes(5 * 1024);
|
final int artifactSize = 5 * 1024;
|
||||||
|
final byte[] random = RandomUtils.nextBytes(artifactSize);
|
||||||
|
|
||||||
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
final DistributionSet ds = testdataFactory.createDistributionSet("");
|
||||||
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
|
||||||
@@ -438,9 +440,9 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
// create two artifacts with identical SHA1 hash
|
// create two artifacts with identical SHA1 hash
|
||||||
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
final Artifact artifact = artifactManagement.create(new ByteArrayInputStream(random),
|
||||||
ds.findFirstModuleByType(osType).get().getId(), "file1", false);
|
ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
|
||||||
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random),
|
final Artifact artifact2 = artifactManagement.create(new ByteArrayInputStream(random),
|
||||||
ds2.findFirstModuleByType(osType).get().getId(), "file1", false);
|
ds2.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize);
|
||||||
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
|
assertThat(artifact.getSha1Hash()).isEqualTo(artifact2.getSha1Hash());
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
@@ -793,7 +795,7 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Expect(type = TargetUpdatedEvent.class, count = 2) })
|
@Expect(type = TargetUpdatedEvent.class, count = 2) })
|
||||||
public void updateTargetAttributesFailsIfTooManyEntries() throws Exception {
|
public void updateTargetAttributesFailsIfTooManyEntries() throws Exception {
|
||||||
final String controllerId = "test123";
|
final String controllerId = "test123";
|
||||||
final int allowedAttributes = 10;
|
final int allowedAttributes = quotaManagement.getMaxAttributeEntriesPerTarget();
|
||||||
testdataFactory.createTarget(controllerId);
|
testdataFactory.createTarget(controllerId);
|
||||||
|
|
||||||
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> securityRule
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> securityRule
|
||||||
@@ -880,4 +882,69 @@ public class ControllerManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(messages.get(0)).as("Message of action-status").isEqualTo("proceeding message 2");
|
assertThat(messages.get(0)).as("Message of action-status").isEqualTo("proceeding message 2");
|
||||||
assertThat(messages.get(1)).as("Message of action-status").isEqualTo("proceeding message 1");
|
assertThat(messages.get(1)).as("Message of action-status").isEqualTo("proceeding message 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the quota specifying the maximum number of status entries per action is enforced.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6) })
|
||||||
|
public void addActionStatusUpdatesUntilQuotaIsExceeded() {
|
||||||
|
|
||||||
|
// any distribution set assignment causes 1 status entity to be created
|
||||||
|
final int maxStatusEntries = quotaManagement.getMaxStatusEntriesPerAction() - 1;
|
||||||
|
|
||||||
|
// test for informational status
|
||||||
|
final Long actionId1 = assignDistributionSet(testdataFactory.createDistributionSet("ds1"),
|
||||||
|
testdataFactory.createTargets(1, "t1")).getActions().get(0);
|
||||||
|
assertThat(actionId1).isNotNull();
|
||||||
|
for (int i = 0; i < maxStatusEntries; ++i) {
|
||||||
|
controllerManagement.addInformationalActionStatus(entityFactory.actionStatus().create(actionId1)
|
||||||
|
.status(Status.WARNING).message("Msg " + i).occurredAt(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> controllerManagement
|
||||||
|
.addInformationalActionStatus(entityFactory.actionStatus().create(actionId1).status(Status.WARNING)));
|
||||||
|
|
||||||
|
// test for update status (and mixed case)
|
||||||
|
final Long actionId2 = assignDistributionSet(testdataFactory.createDistributionSet("ds2"),
|
||||||
|
testdataFactory.createTargets(1, "t2")).getActions().get(0);
|
||||||
|
assertThat(actionId2).isNotEqualTo(actionId1);
|
||||||
|
for (int i = 0; i < maxStatusEntries; ++i) {
|
||||||
|
controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId2)
|
||||||
|
.status(Status.WARNING).message("Msg " + i).occurredAt(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> controllerManagement
|
||||||
|
.addInformationalActionStatus(entityFactory.actionStatus().create(actionId2).status(Status.WARNING)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the quota specifying the maximum number of messages per action status is enforced.")
|
||||||
|
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
|
||||||
|
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1),
|
||||||
|
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||||
|
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3) })
|
||||||
|
public void createActionStatusWithTooManyMessages() {
|
||||||
|
|
||||||
|
final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus();
|
||||||
|
|
||||||
|
final Long actionId = assignDistributionSet(testdataFactory.createDistributionSet("ds1"),
|
||||||
|
testdataFactory.createTargets(1)).getActions().get(0);
|
||||||
|
assertThat(actionId).isNotNull();
|
||||||
|
|
||||||
|
final List<String> messages = Lists.newArrayList();
|
||||||
|
IntStream.range(0, maxMessages).forEach(i -> messages.add(i, "msg"));
|
||||||
|
|
||||||
|
assertThat(controllerManagement.addInformationalActionStatus(
|
||||||
|
entityFactory.actionStatus().create(actionId).messages(messages).status(Status.WARNING))).isNotNull();
|
||||||
|
|
||||||
|
messages.add("msg");
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> controllerManagement.addInformationalActionStatus(
|
||||||
|
entityFactory.actionStatus().create(actionId).messages(messages).status(Status.WARNING)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.ActionStatusFields;
|
import org.eclipse.hawkbit.repository.ActionStatusFields;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
@@ -35,6 +36,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
|||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||||
@@ -171,6 +173,37 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(actions.getContent().get(0).getId()).as("Action of target").isEqualTo(actionId);
|
assertThat(actions.getContent().get(0).getId()).as("Action of target").isEqualTo(actionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Test verifies that the 'max actions per target' quota is enforced if the assigned distribution set is changed permanently.")
|
||||||
|
public void changeDistributionSetAssignmentUntilMaxActionsPerTargetQuotaIsExceeded() {
|
||||||
|
|
||||||
|
final int maxActions = quotaManagement.getMaxActionsPerTarget();
|
||||||
|
final List<Target> testTargets = testdataFactory.createTargets(1);
|
||||||
|
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||||
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||||
|
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
|
||||||
|
|
||||||
|
IntStream.range(0, maxActions).forEach(i -> {
|
||||||
|
assignDistributionSet(i % 2 == 0 ? ds1 : ds2, testTargets);
|
||||||
|
});
|
||||||
|
|
||||||
|
// change the distribution set one last time to trigger a quota hit
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> assignDistributionSet(ds3, testTargets));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Assigns the same distribution set to many targets until the 'max targets per manual assignment' quota is exceeded.")
|
||||||
|
public void assignDistributionSetUntilQuotaIsExceeded() {
|
||||||
|
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerManualAssignment();
|
||||||
|
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
assignDistributionSet(ds, testdataFactory.createTargets(maxTargets, "ok"));
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> assignDistributionSet(ds, testdataFactory.createTargets(maxTargets + 1, "fail")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Test verifies that action-states of an action are found by using id-based search.")
|
@Description("Test verifies that action-states of an action are found by using id-based search.")
|
||||||
public void findActionStatusByActionId() {
|
public void findActionStatusByActionId() {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
|
|||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||||
@@ -42,6 +43,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSe
|
|||||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
|
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||||
@@ -333,6 +335,51 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
|
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies the enforcement of the metadata quota per distribution set.")
|
||||||
|
public void createDistributionSetMetadataUntilQuotaIsExceeded() {
|
||||||
|
|
||||||
|
// add meta data one by one
|
||||||
|
final DistributionSet ds1 = testdataFactory.createDistributionSet("ds1");
|
||||||
|
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerDistributionSet();
|
||||||
|
for (int i = 0; i < maxMetaData; ++i) {
|
||||||
|
assertThat((JpaDistributionSetMetadata) createDistributionSetMetadata(ds1.getId(),
|
||||||
|
new JpaDistributionSetMetadata("k" + i, ds1, "v" + i))).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
// quota exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> createDistributionSetMetadata(ds1.getId(), createDistributionSetMetadata(ds1.getId(),
|
||||||
|
new JpaDistributionSetMetadata("k" + maxMetaData, ds1, "v" + maxMetaData))));
|
||||||
|
|
||||||
|
// add multiple meta data entries at once
|
||||||
|
final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2");
|
||||||
|
final List<MetaData> metaData2 = new ArrayList<>();
|
||||||
|
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||||
|
metaData2.add(new JpaDistributionSetMetadata("k" + i, ds2, "v" + i));
|
||||||
|
}
|
||||||
|
// verify quota is exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> createDistributionSetMetadata(ds2.getId(), metaData2));
|
||||||
|
|
||||||
|
// add some meta data entries
|
||||||
|
final DistributionSet ds3 = testdataFactory.createDistributionSet("ds3");
|
||||||
|
final int firstHalf = Math.round(maxMetaData / 2);
|
||||||
|
for (int i = 0; i < firstHalf; ++i) {
|
||||||
|
createDistributionSetMetadata(ds3.getId(), new JpaDistributionSetMetadata("k" + i, ds3, "v" + i));
|
||||||
|
}
|
||||||
|
// add too many data entries
|
||||||
|
final int secondHalf = maxMetaData - firstHalf;
|
||||||
|
final List<MetaData> metaData3 = new ArrayList<>();
|
||||||
|
for (int i = 0; i < secondHalf + 1; ++i) {
|
||||||
|
metaData3.add(new JpaDistributionSetMetadata("kk" + i, ds3, "vv" + i));
|
||||||
|
}
|
||||||
|
// verify quota is exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> createDistributionSetMetadata(ds3.getId(), metaData3));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Ensures that distribution sets can assigned and unassigned to a distribution set tag.")
|
@Description("Ensures that distribution sets can assigned and unassigned to a distribution set tag.")
|
||||||
public void assignAndUnassignDistributionSetToTag() {
|
public void assignAndUnassignDistributionSetToTag() {
|
||||||
@@ -400,8 +447,8 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
@Description("Ensures that it is not possible to add a software module that is not defined of the DS's type.")
|
@Description("Ensures that it is not possible to add a software module that is not defined of the DS's type.")
|
||||||
public void updateDistributionSetUnsupportedModuleFails() {
|
public void updateDistributionSetUnsupportedModuleFails() {
|
||||||
final DistributionSet set = distributionSetManagement
|
final DistributionSet set = distributionSetManagement
|
||||||
.create(entityFactory.distributionSet().create().name("agent-hub2")
|
.create(entityFactory
|
||||||
.version(
|
.distributionSet().create().name("agent-hub2").version(
|
||||||
"1.0.5")
|
"1.0.5")
|
||||||
.type(distributionSetTypeManagement.create(entityFactory.distributionSetType().create()
|
.type(distributionSetTypeManagement.create(entityFactory.distributionSetType().create()
|
||||||
.key("test").name("test").mandatory(Arrays.asList(osType.getId()))).getKey()));
|
.key("test").name("test").mandatory(Arrays.asList(osType.getId()))).getKey()));
|
||||||
@@ -443,6 +490,46 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(ds.isRequiredMigrationStep()).isTrue();
|
assertThat(ds.isRequiredMigrationStep()).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies the enforcement of the software module quota per distribution set.")
|
||||||
|
public void assignSoftwareModulesUntilQuotaIsExceeded() {
|
||||||
|
|
||||||
|
// create some software modules
|
||||||
|
final int maxModules = quotaManagement.getMaxSoftwareModulesPerDistributionSet();
|
||||||
|
final List<Long> modules = Lists.newArrayList();
|
||||||
|
for (int i = 0; i < maxModules + 1; ++i) {
|
||||||
|
modules.add(testdataFactory.createSoftwareModuleApp("sm" + i).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign software modules one by one
|
||||||
|
final DistributionSet ds1 = testdataFactory.createDistributionSetWithNoSoftwareModules("ds1", "1.0");
|
||||||
|
for (int i = 0; i < maxModules; ++i) {
|
||||||
|
distributionSetManagement.assignSoftwareModules(ds1.getId(), Collections.singletonList(modules.get(i)));
|
||||||
|
}
|
||||||
|
// add one more to cause the quota to be exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> {
|
||||||
|
distributionSetManagement.assignSoftwareModules(ds1.getId(),
|
||||||
|
Collections.singletonList(modules.get(maxModules)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// assign all software modules at once
|
||||||
|
final DistributionSet ds2 = testdataFactory.createDistributionSetWithNoSoftwareModules("ds2", "1.0");
|
||||||
|
// verify quota is exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> distributionSetManagement.assignSoftwareModules(ds2.getId(), modules));
|
||||||
|
|
||||||
|
// assign some software modules
|
||||||
|
final DistributionSet ds3 = testdataFactory.createDistributionSetWithNoSoftwareModules("ds3", "1.0");
|
||||||
|
final int firstHalf = Math.round(maxModules / 2);
|
||||||
|
for (int i = 0; i < firstHalf; ++i) {
|
||||||
|
distributionSetManagement.assignSoftwareModules(ds3.getId(), Collections.singletonList(modules.get(i)));
|
||||||
|
}
|
||||||
|
// assign the remaining modules to cause the quota to be exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> distributionSetManagement
|
||||||
|
.assignSoftwareModules(ds3.getId(), modules.subList(firstHalf, modules.size())));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@WithUser(allSpPermissions = true)
|
@WithUser(allSpPermissions = true)
|
||||||
@Description("Checks that metadata for a distribution set can be updated.")
|
@Description("Checks that metadata for a distribution set can be updated.")
|
||||||
@@ -491,7 +578,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
final Iterator<DistributionSet> dsIterator = buildDistributionSets.iterator();
|
final Iterator<DistributionSet> dsIterator = buildDistributionSets.iterator();
|
||||||
final Iterator<Target> tIterator = buildTargetFixtures.iterator();
|
final Iterator<Target> tIterator = buildTargetFixtures.iterator();
|
||||||
final DistributionSet dsFirst = dsIterator.next();
|
dsIterator.next();
|
||||||
final DistributionSet dsSecond = dsIterator.next();
|
final DistributionSet dsSecond = dsIterator.next();
|
||||||
final DistributionSet dsThree = dsIterator.next();
|
final DistributionSet dsThree = dsIterator.next();
|
||||||
final DistributionSet dsFour = dsIterator.next();
|
final DistributionSet dsFour = dsIterator.next();
|
||||||
@@ -775,7 +862,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
new JpaDistributionSetMetadata("key" + index, ds1, "value" + index));
|
new JpaDistributionSetMetadata("key" + index, ds1, "value" + index));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < 20; index++) {
|
for (int index = 0; index < 8; index++) {
|
||||||
createDistributionSetMetadata(ds2.getId(),
|
createDistributionSetMetadata(ds2.getId(),
|
||||||
new JpaDistributionSetMetadata("key" + index, ds2, "value" + index));
|
new JpaDistributionSetMetadata("key" + index, ds2, "value" + index));
|
||||||
}
|
}
|
||||||
@@ -789,8 +876,8 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(metadataOfDs1.getNumberOfElements()).isEqualTo(10);
|
assertThat(metadataOfDs1.getNumberOfElements()).isEqualTo(10);
|
||||||
assertThat(metadataOfDs1.getTotalElements()).isEqualTo(10);
|
assertThat(metadataOfDs1.getTotalElements()).isEqualTo(10);
|
||||||
|
|
||||||
assertThat(metadataOfDs2.getNumberOfElements()).isEqualTo(20);
|
assertThat(metadataOfDs2.getNumberOfElements()).isEqualTo(8);
|
||||||
assertThat(metadataOfDs2.getTotalElements()).isEqualTo(20);
|
assertThat(metadataOfDs2.getTotalElements()).isEqualTo(8);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -8,17 +8,26 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.validation.ConstraintViolationException;
|
import javax.validation.ConstraintViolationException;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.builder.SoftwareModuleTypeCreate;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
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.DistributionSetTypeCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||||
@@ -28,10 +37,6 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Step;
|
import ru.yandex.qatools.allure.annotations.Step;
|
||||||
@@ -193,7 +198,7 @@ public class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the successfull module update of unused distribution set type which is in fact allowed.")
|
@Description("Tests the successful module update of unused distribution set type which is in fact allowed.")
|
||||||
public void updateUnassignedDistributionSetTypeModules() {
|
public void updateUnassignedDistributionSetTypeModules() {
|
||||||
final DistributionSetType updatableType = distributionSetTypeManagement
|
final DistributionSetType updatableType = distributionSetTypeManagement
|
||||||
.create(entityFactory.distributionSetType().create().key("updatableType").name("to be deleted"));
|
.create(entityFactory.distributionSetType().create().key("updatableType").name("to be deleted"));
|
||||||
@@ -217,6 +222,55 @@ public class DistributionSetTypeManagementTest extends AbstractJpaIntegrationTes
|
|||||||
.containsOnly(runtimeType);
|
.containsOnly(runtimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies that the quota for software module types per distribution set type is enforced as expected.")
|
||||||
|
public void quotaMaxSoftwareModuleTypes() {
|
||||||
|
|
||||||
|
final int quota = quotaManagement.getMaxSoftwareModuleTypesPerDistributionSetType();
|
||||||
|
// create software module types
|
||||||
|
final List<Long> moduleTypeIds = Lists.newArrayList();
|
||||||
|
for (int i = 0; i < quota + 1; ++i) {
|
||||||
|
final SoftwareModuleTypeCreate smCreate = entityFactory.softwareModuleType().create().name("smType_" + i)
|
||||||
|
.description("smType_" + i).maxAssignments(1).colour("blue").key("smType_" + i);
|
||||||
|
moduleTypeIds.add(softwareModuleTypeManagement.create(smCreate).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// assign all types at once
|
||||||
|
final DistributionSetType dsType1 = distributionSetTypeManagement
|
||||||
|
.create(entityFactory.distributionSetType().create().key("dst1").name("dst1"));
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(
|
||||||
|
() -> distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType1.getId(), moduleTypeIds));
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(
|
||||||
|
() -> distributionSetTypeManagement.assignOptionalSoftwareModuleTypes(dsType1.getId(), moduleTypeIds));
|
||||||
|
|
||||||
|
// assign as many mandatory modules as possible
|
||||||
|
final DistributionSetType dsType2 = distributionSetTypeManagement
|
||||||
|
.create(entityFactory.distributionSetType().create().key("dst2").name("dst2"));
|
||||||
|
distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType2.getId(),
|
||||||
|
moduleTypeIds.subList(0, quota));
|
||||||
|
assertThat(distributionSetTypeManagement.get(dsType2.getId())).isNotEmpty();
|
||||||
|
assertThat(distributionSetTypeManagement.get(dsType2.getId()).get().getMandatoryModuleTypes().size())
|
||||||
|
.isEqualTo(quota);
|
||||||
|
// assign one more to trigger the quota exceeded error
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> distributionSetTypeManagement.assignMandatorySoftwareModuleTypes(dsType2.getId(),
|
||||||
|
Collections.singletonList(moduleTypeIds.get(quota))));
|
||||||
|
|
||||||
|
// assign as many optional modules as possible
|
||||||
|
final DistributionSetType dsType3 = distributionSetTypeManagement
|
||||||
|
.create(entityFactory.distributionSetType().create().key("dst3").name("dst3"));
|
||||||
|
distributionSetTypeManagement.assignOptionalSoftwareModuleTypes(dsType3.getId(),
|
||||||
|
moduleTypeIds.subList(0, quota));
|
||||||
|
assertThat(distributionSetTypeManagement.get(dsType3.getId())).isNotEmpty();
|
||||||
|
assertThat(distributionSetTypeManagement.get(dsType3.getId()).get().getOptionalModuleTypes().size())
|
||||||
|
.isEqualTo(quota);
|
||||||
|
// assign one more to trigger the quota exceeded error
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> distributionSetTypeManagement.assignOptionalSoftwareModuleTypes(dsType3.getId(),
|
||||||
|
Collections.singletonList(moduleTypeIds.get(quota))));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Tests the successfull update of used distribution set type meta data which is in fact allowed.")
|
@Description("Tests the successfull update of used distribution set type meta data which is in fact allowed.")
|
||||||
public void updateAssignedDistributionSetTypeMetaData() {
|
public void updateAssignedDistributionSetTypeMetaData() {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -39,6 +42,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||||
@@ -70,9 +74,6 @@ import org.springframework.data.domain.Slice;
|
|||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Direction;
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Step;
|
import ru.yandex.qatools.allure.annotations.Step;
|
||||||
@@ -1257,6 +1258,87 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
validateRolloutActionStatus(myRollout.getId(), expectedTargetCountStatus);
|
validateRolloutActionStatus(myRollout.getId(), expectedTargetCountStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verify that a rollout cannot be created if the 'max targets per rollout group' quota is violated.")
|
||||||
|
public void createRolloutFailsIfQuotaGroupQuotaIsViolated() throws Exception {
|
||||||
|
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
|
||||||
|
final int amountTargetsForRollout = maxTargets + 1;
|
||||||
|
final int amountGroups = 1;
|
||||||
|
final String successCondition = "50";
|
||||||
|
final String errorCondition = "80";
|
||||||
|
final String rolloutName = "rolloutTest";
|
||||||
|
final String targetPrefixName = rolloutName;
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||||
|
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
|
||||||
|
|
||||||
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||||
|
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
||||||
|
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
||||||
|
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||||
|
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> rolloutManagement.create(
|
||||||
|
entityFactory.rollout().create().name(rolloutName).description(rolloutName)
|
||||||
|
.targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet),
|
||||||
|
amountGroups, conditions));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verify that a rollout cannot be created based on group definitions if the 'max targets per rollout group' quota is violated for one of the groups.")
|
||||||
|
public void createRolloutWithGroupDefinitionsFailsIfQuotaGroupQuotaIsViolated() throws Exception {
|
||||||
|
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
|
||||||
|
final int amountTargetsForRollout = maxTargets * 2 + 2;
|
||||||
|
final String rolloutName = "rolloutTest";
|
||||||
|
final String targetPrefixName = rolloutName;
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||||
|
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
|
||||||
|
|
||||||
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
||||||
|
|
||||||
|
// create group definitions
|
||||||
|
final RolloutGroupCreate group1 = entityFactory.rolloutGroup().create().conditions(conditions).name("group1")
|
||||||
|
.targetPercentage(50.0F);
|
||||||
|
final RolloutGroupCreate group2 = entityFactory.rolloutGroup().create().conditions(conditions).name("group2")
|
||||||
|
.targetPercentage(100.0F);
|
||||||
|
|
||||||
|
// group1 exceeds the quota
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> rolloutManagement.create(
|
||||||
|
entityFactory.rollout().create().name(rolloutName).description(rolloutName)
|
||||||
|
.targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet),
|
||||||
|
Arrays.asList(group1, group2), conditions));
|
||||||
|
|
||||||
|
// create group definitions
|
||||||
|
final RolloutGroupCreate group3 = entityFactory.rolloutGroup().create().conditions(conditions).name("group3")
|
||||||
|
.targetPercentage(1.0F);
|
||||||
|
final RolloutGroupCreate group4 = entityFactory.rolloutGroup().create().conditions(conditions).name("group4")
|
||||||
|
.targetPercentage(100.0F);
|
||||||
|
|
||||||
|
// group4 exceeds the quota
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> rolloutManagement.create(
|
||||||
|
entityFactory.rollout().create().name(rolloutName).description(rolloutName)
|
||||||
|
.targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet),
|
||||||
|
Arrays.asList(group3, group4), conditions));
|
||||||
|
|
||||||
|
// create group definitions
|
||||||
|
final RolloutGroupCreate group5 = entityFactory.rolloutGroup().create().conditions(conditions).name("group5")
|
||||||
|
.targetPercentage(33.3F);
|
||||||
|
final RolloutGroupCreate group6 = entityFactory.rolloutGroup().create().conditions(conditions).name("group6")
|
||||||
|
.targetPercentage(66.6F);
|
||||||
|
final RolloutGroupCreate group7 = entityFactory.rolloutGroup().create().conditions(conditions).name("group7")
|
||||||
|
.targetPercentage(100.0F);
|
||||||
|
|
||||||
|
// should work fine
|
||||||
|
assertThat(rolloutManagement.create(
|
||||||
|
entityFactory.rollout().create().name(rolloutName).description(rolloutName)
|
||||||
|
.targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet),
|
||||||
|
Arrays.asList(group5, group6, group7), conditions)).isNotNull();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify the creation and the automatic start of a rollout.")
|
@Description("Verify the creation and the automatic start of a rollout.")
|
||||||
public void createAndAutoStartRollout() throws Exception {
|
public void createAndAutoStartRollout() throws Exception {
|
||||||
@@ -1308,7 +1390,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify the creation of a Rollout with a groups definition.")
|
@Description("Verify the creation of a rollout with a groups definition.")
|
||||||
public void createRolloutWithGroupDefinition() throws Exception {
|
public void createRolloutWithGroupDefinition() throws Exception {
|
||||||
final String rolloutName = "rolloutTest3";
|
final String rolloutName = "rolloutTest3";
|
||||||
|
|
||||||
@@ -1355,7 +1437,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(myRollout.getTotalTargets()).isEqualTo(amountTargetsInGroup1and2 + amountTargetsInGroup1);
|
assertThat(myRollout.getTotalTargets()).isEqualTo(amountTargetsInGroup1and2 + amountTargetsInGroup1);
|
||||||
|
|
||||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(PAGE, myRollout.getId()).getContent();
|
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(PAGE, myRollout.getId()).getContent();
|
||||||
;
|
|
||||||
assertThat(groups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.READY);
|
assertThat(groups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.READY);
|
||||||
assertThat(groups.get(0).getTotalTargets()).isEqualTo(amountTargetsInGroup1);
|
assertThat(groups.get(0).getTotalTargets()).isEqualTo(amountTargetsInGroup1);
|
||||||
|
|
||||||
@@ -1368,7 +1450,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify Exception when a Rollout with Group definition is created that does not address all targets")
|
@Description("Verify rollout creation fails if group definition does not address all targets")
|
||||||
public void createRolloutWithGroupsNotMatchingTargets() throws Exception {
|
public void createRolloutWithGroupsNotMatchingTargets() throws Exception {
|
||||||
final String rolloutName = "rolloutTest4";
|
final String rolloutName = "rolloutTest4";
|
||||||
final int amountTargetsForRollout = 500;
|
final int amountTargetsForRollout = 500;
|
||||||
@@ -1389,7 +1471,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify Exception when a Rollout with Group definition is created that contains an illegal percentage")
|
@Description("Verify rollout creation fails if group definition specifies illegal target percentage")
|
||||||
public void createRolloutWithIllegalPercentage() throws Exception {
|
public void createRolloutWithIllegalPercentage() throws Exception {
|
||||||
final String rolloutName = "rolloutTest6";
|
final String rolloutName = "rolloutTest6";
|
||||||
final int amountTargetsForRollout = 10;
|
final int amountTargetsForRollout = 10;
|
||||||
@@ -1410,18 +1492,18 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Verify Exception when a Rollout is created with too much groups")
|
@Description("Verify rollout creation fails if the 'max rollout groups per rollout' quota is violated.")
|
||||||
public void createRolloutWithIllegalAmountOfGroups() throws Exception {
|
public void createRolloutWithIllegalAmountOfGroups() throws Exception {
|
||||||
final String rolloutName = "rolloutTest5";
|
final String rolloutName = "rolloutTest5";
|
||||||
final int amountTargetsForRollout = 10;
|
final int targets = 10;
|
||||||
final int illegalGroupAmount = 501;
|
final int maxGroups = quotaManagement.getMaxRolloutGroupsPerRollout();
|
||||||
|
|
||||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build();
|
||||||
final RolloutCreate myRollout = generateTargetsAndRollout(rolloutName, amountTargetsForRollout);
|
final RolloutCreate rollout = generateTargetsAndRollout(rolloutName, targets);
|
||||||
|
|
||||||
assertThatExceptionOfType(ValidationException.class)
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
.isThrownBy(() -> rolloutManagement.create(myRollout, illegalGroupAmount, conditions))
|
.isThrownBy(() -> rolloutManagement.create(rollout, maxGroups + 1, conditions))
|
||||||
.withMessageContaining("not be greater than " + quotaManagement.getMaxRolloutGroupsPerRollout());
|
.withMessageContaining("not be greater than " + maxGroups);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -23,6 +24,7 @@ import org.apache.commons.lang3.RandomUtils;
|
|||||||
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
import org.eclipse.hawkbit.repository.builder.SoftwareModuleMetadataCreate;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||||
@@ -361,13 +363,14 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void deleteSoftwareModulesWithSharedArtifact() throws IOException {
|
public void deleteSoftwareModulesWithSharedArtifact() throws IOException {
|
||||||
|
|
||||||
// Init artifact binary data, target and DistributionSets
|
// Init artifact binary data, target and DistributionSets
|
||||||
final byte[] source = RandomUtils.nextBytes(1024);
|
final int artifactSize = 1024;
|
||||||
|
final byte[] source = RandomUtils.nextBytes(artifactSize);
|
||||||
|
|
||||||
// [STEP1]: Create SoftwareModuleX and add a new ArtifactX
|
// [STEP1]: Create SoftwareModuleX and add a new ArtifactX
|
||||||
SoftwareModule moduleX = createSoftwareModuleWithArtifacts(osType, "modulex", "v1.0", 0);
|
SoftwareModule moduleX = createSoftwareModuleWithArtifacts(osType, "modulex", "v1.0", 0);
|
||||||
|
|
||||||
// [STEP2]: Create newArtifactX and add it to SoftwareModuleX
|
// [STEP2]: Create newArtifactX and add it to SoftwareModuleX
|
||||||
artifactManagement.create(new ByteArrayInputStream(source), moduleX.getId(), "artifactx", false);
|
artifactManagement.create(new ByteArrayInputStream(source), moduleX.getId(), "artifactx", false, artifactSize);
|
||||||
moduleX = softwareModuleManagement.get(moduleX.getId()).get();
|
moduleX = softwareModuleManagement.get(moduleX.getId()).get();
|
||||||
final Artifact artifactX = moduleX.getArtifacts().iterator().next();
|
final Artifact artifactX = moduleX.getArtifacts().iterator().next();
|
||||||
|
|
||||||
@@ -375,7 +378,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
SoftwareModule moduleY = createSoftwareModuleWithArtifacts(osType, "moduley", "v1.0", 0);
|
SoftwareModule moduleY = createSoftwareModuleWithArtifacts(osType, "moduley", "v1.0", 0);
|
||||||
|
|
||||||
// [STEP4]: Assign the same ArtifactX to SoftwareModuleY
|
// [STEP4]: Assign the same ArtifactX to SoftwareModuleY
|
||||||
artifactManagement.create(new ByteArrayInputStream(source), moduleY.getId(), "artifactx", false);
|
artifactManagement.create(new ByteArrayInputStream(source), moduleY.getId(), "artifactx", false, artifactSize);
|
||||||
moduleY = softwareModuleManagement.get(moduleY.getId()).get();
|
moduleY = softwareModuleManagement.get(moduleY.getId()).get();
|
||||||
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
|
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
|
||||||
|
|
||||||
@@ -403,20 +406,21 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void deleteMultipleSoftwareModulesWhichShareAnArtifact() throws IOException {
|
public void deleteMultipleSoftwareModulesWhichShareAnArtifact() throws IOException {
|
||||||
|
|
||||||
// Init artifact binary data, target and DistributionSets
|
// Init artifact binary data, target and DistributionSets
|
||||||
final byte[] source = RandomUtils.nextBytes(1024);
|
final int artifactSize = 1024;
|
||||||
|
final byte[] source = RandomUtils.nextBytes(artifactSize);
|
||||||
final Target target = testdataFactory.createTarget();
|
final Target target = testdataFactory.createTarget();
|
||||||
|
|
||||||
// [STEP1]: Create SoftwareModuleX and add a new ArtifactX
|
// [STEP1]: Create SoftwareModuleX and add a new ArtifactX
|
||||||
SoftwareModule moduleX = createSoftwareModuleWithArtifacts(osType, "modulex", "v1.0", 0);
|
SoftwareModule moduleX = createSoftwareModuleWithArtifacts(osType, "modulex", "v1.0", 0);
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(source), moduleX.getId(), "artifactx", false);
|
artifactManagement.create(new ByteArrayInputStream(source), moduleX.getId(), "artifactx", false, artifactSize);
|
||||||
moduleX = softwareModuleManagement.get(moduleX.getId()).get();
|
moduleX = softwareModuleManagement.get(moduleX.getId()).get();
|
||||||
final Artifact artifactX = moduleX.getArtifacts().iterator().next();
|
final Artifact artifactX = moduleX.getArtifacts().iterator().next();
|
||||||
|
|
||||||
// [STEP2]: Create SoftwareModuleY and add the same ArtifactX
|
// [STEP2]: Create SoftwareModuleY and add the same ArtifactX
|
||||||
SoftwareModule moduleY = createSoftwareModuleWithArtifacts(osType, "moduley", "v1.0", 0);
|
SoftwareModule moduleY = createSoftwareModuleWithArtifacts(osType, "moduley", "v1.0", 0);
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(source), moduleY.getId(), "artifactx", false);
|
artifactManagement.create(new ByteArrayInputStream(source), moduleY.getId(), "artifactx", false, artifactSize);
|
||||||
moduleY = softwareModuleManagement.get(moduleY.getId()).get();
|
moduleY = softwareModuleManagement.get(moduleY.getId()).get();
|
||||||
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
|
final Artifact artifactY = moduleY.getArtifacts().iterator().next();
|
||||||
|
|
||||||
@@ -462,9 +466,10 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
SoftwareModule softwareModule = softwareModuleManagement.create(entityFactory.softwareModule().create()
|
SoftwareModule softwareModule = softwareModuleManagement.create(entityFactory.softwareModule().create()
|
||||||
.type(type).name(name).version(version).description("description of artifact " + name));
|
.type(type).name(name).version(version).description("description of artifact " + name));
|
||||||
|
|
||||||
|
final int artifactSize = 5 * 1024;
|
||||||
for (int i = 0; i < numberArtifacts; i++) {
|
for (int i = 0; i < numberArtifacts; i++) {
|
||||||
artifactManagement.create(new RandomGeneratedInputStream(5 * 1024), softwareModule.getId(),
|
artifactManagement.create(new RandomGeneratedInputStream(artifactSize), softwareModule.getId(),
|
||||||
"file" + (i + 1), false);
|
"file" + (i + 1), false, artifactSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify correct Creation of SoftwareModule and corresponding artifacts
|
// Verify correct Creation of SoftwareModule and corresponding artifacts
|
||||||
@@ -652,14 +657,58 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(softwareModuleMetadata.get(0).getEntityId()).isEqualTo(ah.getId());
|
assertThat(softwareModuleMetadata.get(0).getEntityId()).isEqualTo(ah.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Verifies the enforcement of the metadata quota per software module.")
|
||||||
|
public void createSoftwareModuleMetadataUntilQuotaIsExceeded() {
|
||||||
|
|
||||||
|
// add meta data one by one
|
||||||
|
final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1");
|
||||||
|
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerSoftwareModule();
|
||||||
|
for (int i = 0; i < maxMetaData; ++i) {
|
||||||
|
softwareModuleManagement.createMetaData(
|
||||||
|
entityFactory.softwareModuleMetadata().create(module.getId()).key("k" + i).value("v" + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// quota exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata()
|
||||||
|
.create(module.getId()).key("k" + maxMetaData).value("v" + maxMetaData)));
|
||||||
|
|
||||||
|
// add multiple meta data entries at once
|
||||||
|
final SoftwareModule module2 = testdataFactory.createSoftwareModuleApp("m2");
|
||||||
|
final List<SoftwareModuleMetadataCreate> create = new ArrayList<>();
|
||||||
|
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||||
|
create.add(entityFactory.softwareModuleMetadata().create(module2.getId()).key("k" + i).value("v" + i));
|
||||||
|
}
|
||||||
|
// quota exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> softwareModuleManagement.createMetaData(create));
|
||||||
|
|
||||||
|
// add some meta data entries
|
||||||
|
final SoftwareModule module3 = testdataFactory.createSoftwareModuleApp("m3");
|
||||||
|
final int firstHalf = Math.round(maxMetaData / 2);
|
||||||
|
for (int i = 0; i < firstHalf; ++i) {
|
||||||
|
softwareModuleManagement.createMetaData(
|
||||||
|
entityFactory.softwareModuleMetadata().create(module3.getId()).key("k" + i).value("v" + i));
|
||||||
|
}
|
||||||
|
// add too many data entries
|
||||||
|
final int secondHalf = maxMetaData - firstHalf;
|
||||||
|
final List<SoftwareModuleMetadataCreate> create2 = new ArrayList<>();
|
||||||
|
for (int i = 0; i < secondHalf + 1; ++i) {
|
||||||
|
create2.add(entityFactory.softwareModuleMetadata().create(module3.getId()).key("kk" + i).value("vv" + i));
|
||||||
|
}
|
||||||
|
// quota exceeded
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class)
|
||||||
|
.isThrownBy(() -> softwareModuleManagement.createMetaData(create2));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Checks that metadata for a software module cannot be created for an existing key.")
|
@Description("Checks that metadata for a software module cannot be created for an existing key.")
|
||||||
public void createSoftwareModuleMetadataFailsIfKeyExists() {
|
public void createSoftwareModuleMetadataFailsIfKeyExists() {
|
||||||
|
|
||||||
final String knownKey1 = "myKnownKey1";
|
final String knownKey1 = "myKnownKey1";
|
||||||
final String knownValue1 = "myKnownValue1";
|
final String knownValue1 = "myKnownValue1";
|
||||||
final String knownValue2 = "myKnownValue2";
|
|
||||||
|
|
||||||
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
|
final SoftwareModule ah = testdataFactory.createSoftwareModuleApp();
|
||||||
|
|
||||||
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey1)
|
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(ah.getId()).key(knownKey1)
|
||||||
@@ -765,41 +814,43 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
public void findAllSoftwareModuleMetadataBySwId() {
|
public void findAllSoftwareModuleMetadataBySwId() {
|
||||||
|
|
||||||
final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp();
|
final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp();
|
||||||
|
final int metadataCountSw1 = 8;
|
||||||
|
|
||||||
final SoftwareModule sw2 = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sw2 = testdataFactory.createSoftwareModuleOs();
|
||||||
|
final int metadataCountSw2 = 10;
|
||||||
|
|
||||||
for (int index = 0; index < 10; index++) {
|
for (int index = 0; index < metadataCountSw1; index++) {
|
||||||
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(sw1.getId())
|
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(sw1.getId())
|
||||||
.key("key" + index).value("value" + index).targetVisible(true));
|
.key("key" + index).value("value" + index).targetVisible(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int index = 0; index < 20; index++) {
|
for (int index = 0; index < metadataCountSw2; index++) {
|
||||||
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(sw2.getId())
|
softwareModuleManagement.createMetaData(entityFactory.softwareModuleMetadata().create(sw2.getId())
|
||||||
.key("key" + index).value("value" + index).targetVisible(false));
|
.key("key" + index).value("value" + index).targetVisible(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
Page<SoftwareModuleMetadata> metadataOfSw1 = softwareModuleManagement
|
Page<SoftwareModuleMetadata> metadataSw1 = softwareModuleManagement
|
||||||
.findMetaDataBySoftwareModuleId(new PageRequest(0, 100), sw1.getId());
|
.findMetaDataBySoftwareModuleId(new PageRequest(0, 100), sw1.getId());
|
||||||
|
|
||||||
Page<SoftwareModuleMetadata> metadataOfSw2 = softwareModuleManagement
|
Page<SoftwareModuleMetadata> metadataSw2 = softwareModuleManagement
|
||||||
.findMetaDataBySoftwareModuleId(new PageRequest(0, 100), sw2.getId());
|
.findMetaDataBySoftwareModuleId(new PageRequest(0, 100), sw2.getId());
|
||||||
|
|
||||||
assertThat(metadataOfSw1.getNumberOfElements()).isEqualTo(10);
|
assertThat(metadataSw1.getNumberOfElements()).isEqualTo(metadataCountSw1);
|
||||||
assertThat(metadataOfSw1.getTotalElements()).isEqualTo(10);
|
assertThat(metadataSw1.getTotalElements()).isEqualTo(metadataCountSw1);
|
||||||
|
|
||||||
assertThat(metadataOfSw2.getNumberOfElements()).isEqualTo(20);
|
assertThat(metadataSw2.getNumberOfElements()).isEqualTo(metadataCountSw2);
|
||||||
assertThat(metadataOfSw2.getTotalElements()).isEqualTo(20);
|
assertThat(metadataSw2.getTotalElements()).isEqualTo(metadataCountSw2);
|
||||||
|
|
||||||
metadataOfSw1 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(new PageRequest(0, 100),
|
metadataSw1 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(new PageRequest(0, 100),
|
||||||
sw1.getId());
|
sw1.getId());
|
||||||
|
|
||||||
metadataOfSw2 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(new PageRequest(0, 100),
|
metadataSw2 = softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible(new PageRequest(0, 100),
|
||||||
sw2.getId());
|
sw2.getId());
|
||||||
|
|
||||||
assertThat(metadataOfSw1.getNumberOfElements()).isEqualTo(10);
|
assertThat(metadataSw1.getNumberOfElements()).isEqualTo(metadataCountSw1);
|
||||||
assertThat(metadataOfSw1.getTotalElements()).isEqualTo(10);
|
assertThat(metadataSw1.getTotalElements()).isEqualTo(metadataCountSw1);
|
||||||
|
|
||||||
assertThat(metadataOfSw2.getNumberOfElements()).isEqualTo(0);
|
assertThat(metadataSw2.getNumberOfElements()).isEqualTo(0);
|
||||||
assertThat(metadataOfSw2.getTotalElements()).isEqualTo(0);
|
assertThat(metadataSw2.getTotalElements()).isEqualTo(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,13 +134,13 @@ public class SystemManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
private void createTestArtifact(final byte[] random) {
|
private void createTestArtifact(final byte[] random) {
|
||||||
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
|
||||||
|
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false);
|
artifactManagement.create(new ByteArrayInputStream(random), sm.getId(), "file1", false, random.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDeletedTestArtifact(final byte[] random) {
|
private void createDeletedTestArtifact(final byte[] random) {
|
||||||
final DistributionSet ds = testdataFactory.createDistributionSet("deleted garbage", true);
|
final DistributionSet ds = testdataFactory.createDistributionSet("deleted garbage", true);
|
||||||
ds.getModules().stream().forEach(module -> {
|
ds.getModules().stream().forEach(module -> {
|
||||||
artifactManagement.create(new ByteArrayInputStream(random), module.getId(), "file1", false);
|
artifactManagement.create(new ByteArrayInputStream(random), module.getId(), "file1", false, random.length);
|
||||||
softwareModuleManagement.delete(module.getId());
|
softwareModuleManagement.delete(module.getId());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
@@ -24,6 +26,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE
|
|||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||||
import org.eclipse.hawkbit.repository.model.Target;
|
import org.eclipse.hawkbit.repository.model.Target;
|
||||||
@@ -34,8 +37,6 @@ import org.junit.Test;
|
|||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
import ru.yandex.qatools.allure.annotations.Description;
|
import ru.yandex.qatools.allure.annotations.Description;
|
||||||
import ru.yandex.qatools.allure.annotations.Features;
|
import ru.yandex.qatools.allure.annotations.Features;
|
||||||
import ru.yandex.qatools.allure.annotations.Stories;
|
import ru.yandex.qatools.allure.annotations.Stories;
|
||||||
@@ -97,6 +98,20 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
|||||||
targetFilterQueryManagement.getByName(filterName).get());
|
targetFilterQueryManagement.getByName(filterName).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Create a target filter query with an auto-assign distribution set and a query string that addresses too many targets.")
|
||||||
|
public void createTargetFilterQueryThatExceedsQuota() {
|
||||||
|
|
||||||
|
// create targets
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target%s");
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
// creation is supposed to work as there is no distribution set
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement.create(
|
||||||
|
entityFactory.targetFilterQuery().create().name("testfilter").set(set.getId()).query("name==target*")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Test searching a target filter query.")
|
@Description("Test searching a target filter query.")
|
||||||
public void searchTargetFilterQuery() {
|
public void searchTargetFilterQuery() {
|
||||||
@@ -181,6 +196,43 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Assigns a distribution set to an existing filter query and verifies that the quota 'max targets per auto assignment' is enforced.")
|
||||||
|
public void assignDistributionSetToTargetFilterQueryThatExceedsQuota() {
|
||||||
|
|
||||||
|
// create targets
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target%s");
|
||||||
|
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
// creation is supposed to work as there is no distribution set
|
||||||
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||||
|
.create(entityFactory.targetFilterQuery().create().name("testfilter").query("name==target*"));
|
||||||
|
|
||||||
|
// assigning a distribution set is supposed to fail as the query
|
||||||
|
// addresses too many targets
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement
|
||||||
|
.updateAutoAssignDS(targetFilterQuery.getId(), distributionSet.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Description("Updates an existing filter query with a query string that addresses too many targets.")
|
||||||
|
public void updateTargetFilterQueryWithQueryThatExceedsQuota() {
|
||||||
|
|
||||||
|
// create targets
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment();
|
||||||
|
testdataFactory.createTargets(maxTargets + 1, "target%s");
|
||||||
|
final DistributionSet set = testdataFactory.createDistributionSet();
|
||||||
|
|
||||||
|
// creation is supposed to work as the query does not exceed the quota
|
||||||
|
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.create(
|
||||||
|
entityFactory.targetFilterQuery().create().name("testfilter").set(set.getId()).query("name==foo"));
|
||||||
|
|
||||||
|
// update with a query string that addresses too many targets
|
||||||
|
assertThatExceptionOfType(QuotaExceededException.class).isThrownBy(() -> targetFilterQueryManagement
|
||||||
|
.update(entityFactory.targetFilterQuery().update(targetFilterQuery.getId()).query("name==target*")));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Description("Test removing distribution set while it has a relation to a target filter query")
|
@Description("Test removing distribution set while it has a relation to a target filter query")
|
||||||
public void removeAssignDistributionSet() {
|
public void removeAssignDistributionSet() {
|
||||||
|
|||||||
@@ -10,6 +10,15 @@
|
|||||||
# Quota - START
|
# Quota - START
|
||||||
hawkbit.server.security.dos.maxStatusEntriesPerAction=10
|
hawkbit.server.security.dos.maxStatusEntriesPerAction=10
|
||||||
hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10
|
hawkbit.server.security.dos.maxAttributeEntriesPerTarget=10
|
||||||
|
hawkbit.server.security.dos.maxMetaDataEntriesPerSoftwareModule=10
|
||||||
|
hawkbit.server.security.dos.maxRolloutGroupsPerRollout=20
|
||||||
|
hawkbit.server.security.dos.maxMessagesPerActionStatus=10
|
||||||
|
hawkbit.server.security.dos.maxMetaDataEntriesPerDistributionSet=10
|
||||||
|
hawkbit.server.security.dos.maxSoftwareModuleTypesPerDistributionSetType=10
|
||||||
|
hawkbit.server.security.dos.maxSoftwareModulesPerDistributionSet=10
|
||||||
|
hawkbit.server.security.dos.maxArtifactsPerSoftwareModule=10
|
||||||
|
hawkbit.server.security.dos.maxTargetsPerRolloutGroup=1000
|
||||||
|
hawkbit.server.security.dos.maxArtifactSize=1000000
|
||||||
# Quota - END
|
# Quota - END
|
||||||
|
|
||||||
# Debug utility functions - START
|
# Debug utility functions - START
|
||||||
|
|||||||
@@ -276,7 +276,11 @@ public abstract class AbstractIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected DistributionSetMetadata createDistributionSetMetadata(final Long dsId, final MetaData md) {
|
protected DistributionSetMetadata createDistributionSetMetadata(final Long dsId, final MetaData md) {
|
||||||
return distributionSetManagement.createMetaData(dsId, Collections.singletonList(md)).get(0);
|
return createDistributionSetMetadata(dsId, Collections.singletonList(md)).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<DistributionSetMetadata> createDistributionSetMetadata(final Long dsId, final List<MetaData> md) {
|
||||||
|
return distributionSetManagement.createMetaData(dsId, md);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Long getOsModule(final DistributionSet ds) {
|
protected Long getOsModule(final DistributionSet ds) {
|
||||||
|
|||||||
@@ -463,8 +463,10 @@ public class TestdataFactory {
|
|||||||
public List<Artifact> createArtifacts(final Long moduleId) {
|
public List<Artifact> createArtifacts(final Long moduleId) {
|
||||||
final List<Artifact> artifacts = new ArrayList<>();
|
final List<Artifact> artifacts = new ArrayList<>();
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
final InputStream stubInputStream = IOUtils.toInputStream("some test data" + i, Charset.forName("UTF-8"));
|
final String artifactData = "some test data" + i;
|
||||||
artifacts.add(artifactManagement.create(stubInputStream, moduleId, "filename" + i, false));
|
final InputStream stubInputStream = IOUtils.toInputStream(artifactData, Charset.forName("UTF-8"));
|
||||||
|
artifacts.add(
|
||||||
|
artifactManagement.create(stubInputStream, moduleId, "filename" + i, false, artifactData.length()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,58 @@ public class HawkbitSecurityProperties {
|
|||||||
*/
|
*/
|
||||||
private int maxMessagesPerActionStatus = 50;
|
private int maxMessagesPerActionStatus = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of meta data entries per software module
|
||||||
|
*/
|
||||||
|
private int maxMetaDataEntriesPerSoftwareModule = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of meta data entries per distribution set
|
||||||
|
*/
|
||||||
|
private int maxMetaDataEntriesPerDistributionSet = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of software modules per distribution set
|
||||||
|
*/
|
||||||
|
private int maxSoftwareModulesPerDistributionSet = 100;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of software modules per distribution set
|
||||||
|
*/
|
||||||
|
private int maxSoftwareModuleTypesPerDistributionSetType = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of artifacts per software module
|
||||||
|
*/
|
||||||
|
private int maxArtifactsPerSoftwareModule = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of targets per rollout group
|
||||||
|
*/
|
||||||
|
private int maxTargetsPerRolloutGroup = 20000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of targets per rollout group
|
||||||
|
*/
|
||||||
|
private int maxActionsPerTarget = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of targets for a manual distribution set assignment.
|
||||||
|
* Must be greater than 1000.
|
||||||
|
*/
|
||||||
|
private int maxTargetsPerManualAssignment = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of targets for an automatic distribution set
|
||||||
|
* assignment
|
||||||
|
*/
|
||||||
|
private int maxTargetsPerAutoAssignment = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum size of artifacts in bytes.
|
||||||
|
*/
|
||||||
|
private long maxArtifactSize = 1_000_000_000;
|
||||||
|
|
||||||
private final Filter filter = new Filter();
|
private final Filter filter = new Filter();
|
||||||
private final Filter uiFilter = new Filter();
|
private final Filter uiFilter = new Filter();
|
||||||
|
|
||||||
@@ -157,6 +209,87 @@ public class HawkbitSecurityProperties {
|
|||||||
this.maxRolloutGroupsPerRollout = maxRolloutGroupsPerRollout;
|
this.maxRolloutGroupsPerRollout = maxRolloutGroupsPerRollout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMaxMetaDataEntriesPerSoftwareModule() {
|
||||||
|
return maxMetaDataEntriesPerSoftwareModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxMetaDataEntriesPerSoftwareModule(final int maxMetaDataEntriesPerSoftwareModule) {
|
||||||
|
this.maxMetaDataEntriesPerSoftwareModule = maxMetaDataEntriesPerSoftwareModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxMetaDataEntriesPerDistributionSet() {
|
||||||
|
return maxMetaDataEntriesPerDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxMetaDataEntriesPerDistributionSet(final int maxMetaDataEntriesPerDistributionSet) {
|
||||||
|
this.maxMetaDataEntriesPerDistributionSet = maxMetaDataEntriesPerDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxSoftwareModulesPerDistributionSet() {
|
||||||
|
return maxSoftwareModulesPerDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxSoftwareModulesPerDistributionSet(final int maxSoftwareModulesPerDistributionSet) {
|
||||||
|
this.maxSoftwareModulesPerDistributionSet = maxSoftwareModulesPerDistributionSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxSoftwareModuleTypesPerDistributionSetType() {
|
||||||
|
return maxSoftwareModuleTypesPerDistributionSetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxSoftwareModuleTypesPerDistributionSetType(
|
||||||
|
final int maxSoftwareModuleTypesPerDistributionSetType) {
|
||||||
|
this.maxSoftwareModuleTypesPerDistributionSetType = maxSoftwareModuleTypesPerDistributionSetType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxArtifactsPerSoftwareModule() {
|
||||||
|
return maxArtifactsPerSoftwareModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxArtifactsPerSoftwareModule(final int maxArtifactsPerSoftwareModule) {
|
||||||
|
this.maxArtifactsPerSoftwareModule = maxArtifactsPerSoftwareModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxTargetsPerRolloutGroup() {
|
||||||
|
return maxTargetsPerRolloutGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxTargetsPerRolloutGroup(final int maxTargetsPerRolloutGroup) {
|
||||||
|
this.maxTargetsPerRolloutGroup = maxTargetsPerRolloutGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxActionsPerTarget() {
|
||||||
|
return maxActionsPerTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxActionsPerTarget(final int maxActionsPerTarget) {
|
||||||
|
this.maxActionsPerTarget = maxActionsPerTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxTargetsPerManualAssignment() {
|
||||||
|
return maxTargetsPerManualAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxTargetsPerManualAssignment(final int maxTargetsPerManualAssignment) {
|
||||||
|
this.maxTargetsPerManualAssignment = maxTargetsPerManualAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxTargetsPerAutoAssignment() {
|
||||||
|
return maxTargetsPerAutoAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxTargetsPerAutoAssignment(final int maxTargetsPerAutoAssignment) {
|
||||||
|
this.maxTargetsPerAutoAssignment = maxTargetsPerAutoAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxArtifactSize(final long maxArtifactSize) {
|
||||||
|
this.maxArtifactSize = maxArtifactSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMaxArtifactSize() {
|
||||||
|
return maxArtifactSize;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for hawkBits DOS prevention filter. This is usually an
|
* Configuration for hawkBits DOS prevention filter. This is usually an
|
||||||
* infrastructure topic (e.g. Web Application Firewall (WAF)) but might
|
* infrastructure topic (e.g. Web Application Firewall (WAF)) but might
|
||||||
@@ -179,13 +312,13 @@ public class HawkbitSecurityProperties {
|
|||||||
* # Maximum number of allowed REST read/GET requests per second per
|
* # Maximum number of allowed REST read/GET requests per second per
|
||||||
* client IP.
|
* client IP.
|
||||||
*/
|
*/
|
||||||
int maxRead = 200;
|
private int maxRead = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of allowed REST write/(PUT/POST/etc.) requests per
|
* Maximum number of allowed REST write/(PUT/POST/etc.) requests per
|
||||||
* second per client IP.
|
* second per client IP.
|
||||||
*/
|
*/
|
||||||
int maxWrite = 50;
|
private int maxWrite = 50;
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
@@ -220,5 +353,6 @@ public class HawkbitSecurityProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.ArtifactManagement;
|
|||||||
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
import org.eclipse.hawkbit.repository.exception.ArtifactUploadFailedException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
|
import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
|
||||||
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
|
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
|
||||||
|
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||||
import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState;
|
import org.eclipse.hawkbit.ui.artifacts.state.ArtifactUploadState;
|
||||||
@@ -591,7 +592,8 @@ public class UploadConfirmationWindow implements Button.ClickListener {
|
|||||||
customFile.getBaseSoftwareModuleName(), customFile.getBaseSoftwareModuleVersion());
|
customFile.getBaseSoftwareModuleName(), customFile.getBaseSoftwareModuleVersion());
|
||||||
if (customFile.getFileName().equals(fileName)
|
if (customFile.getFileName().equals(fileName)
|
||||||
&& baseSwModuleNameVersion.equals(baseSoftwareModuleNameVersion)) {
|
&& baseSwModuleNameVersion.equals(baseSoftwareModuleNameVersion)) {
|
||||||
createArtifact(itemId, customFile.getFilePath(), artifactManagement, bSoftwareModule);
|
createArtifact(itemId, customFile.getFilePath(), customFile.getFileSize(), artifactManagement,
|
||||||
|
bSoftwareModule);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refreshArtifactDetailsLayout = checkIfArtifactDetailsDisplayed(bSoftwareModule.getId());
|
refreshArtifactDetailsLayout = checkIfArtifactDetailsDisplayed(bSoftwareModule.getId());
|
||||||
@@ -618,8 +620,8 @@ public class UploadConfirmationWindow implements Button.ClickListener {
|
|||||||
currentUploadResultWindow = null;
|
currentUploadResultWindow = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createArtifact(final String itemId, final String filePath, final ArtifactManagement artifactManagement,
|
private void createArtifact(final String itemId, final String filePath, final long fileSize,
|
||||||
final SoftwareModule baseSw) {
|
final ArtifactManagement artifactManagement, final SoftwareModule baseSw) {
|
||||||
|
|
||||||
final File newFile = new File(filePath);
|
final File newFile = new File(filePath);
|
||||||
final Item item = tableContainer.getItem(itemId);
|
final Item item = tableContainer.getItem(itemId);
|
||||||
@@ -638,11 +640,11 @@ public class UploadConfirmationWindow implements Button.ClickListener {
|
|||||||
try (FileInputStream fis = new FileInputStream(newFile)) {
|
try (FileInputStream fis = new FileInputStream(newFile)) {
|
||||||
|
|
||||||
artifactManagement.create(fis, baseSw.getId(), providedFileName, md5Checksum, sha1Checksum, true,
|
artifactManagement.create(fis, baseSw.getId(), providedFileName, md5Checksum, sha1Checksum, true,
|
||||||
customFile.getMimeType());
|
customFile.getMimeType(), fileSize);
|
||||||
saveUploadStatus(providedFileName, swModuleNameVersion, SPUILabelDefinitions.SUCCESS, "");
|
saveUploadStatus(providedFileName, swModuleNameVersion, SPUILabelDefinitions.SUCCESS, "");
|
||||||
|
|
||||||
} catch (final ArtifactUploadFailedException | InvalidSHA1HashException | InvalidMD5HashException
|
} catch (final ArtifactUploadFailedException | InvalidSHA1HashException | InvalidMD5HashException
|
||||||
| FileNotFoundException e) {
|
| FileNotFoundException | QuotaExceededException e) {
|
||||||
|
|
||||||
saveUploadStatus(providedFileName, swModuleNameVersion, SPUILabelDefinitions.FAILED, e.getMessage());
|
saveUploadStatus(providedFileName, swModuleNameVersion, SPUILabelDefinitions.FAILED, e.getMessage());
|
||||||
LOG.error(ARTIFACT_UPLOAD_EXCEPTION, e);
|
LOG.error(ARTIFACT_UPLOAD_EXCEPTION, e);
|
||||||
|
|||||||
@@ -91,14 +91,17 @@ import com.vaadin.ui.themes.ValoTheme;
|
|||||||
/**
|
/**
|
||||||
* Rollout add or update popup layout.
|
* Rollout add or update popup layout.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({ "squid:MaximumInheritanceDepth", "squid:S2160" })
|
||||||
public class AddUpdateRolloutWindowLayout extends GridLayout {
|
public class AddUpdateRolloutWindowLayout extends GridLayout {
|
||||||
|
|
||||||
private static final long serialVersionUID = 2999293468801479916L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AddUpdateRolloutWindowLayout.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AddUpdateRolloutWindowLayout.class);
|
||||||
|
|
||||||
private static final String MESSAGE_ROLLOUT_FIELD_VALUE_RANGE = "message.rollout.field.value.range";
|
private static final String MESSAGE_ROLLOUT_FIELD_VALUE_RANGE = "message.rollout.field.value.range";
|
||||||
|
|
||||||
|
private static final String MESSAGE_ROLLOUT_MAX_GROUP_SIZE_EXCEEDED = "message.rollout.max.group.size.exceeded";
|
||||||
|
|
||||||
private static final String MESSAGE_ROLLOUT_FILTER_TARGET_EXISTS = "message.rollout.filter.target.exists";
|
private static final String MESSAGE_ROLLOUT_FILTER_TARGET_EXISTS = "message.rollout.filter.target.exists";
|
||||||
|
|
||||||
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
|
private static final String MESSAGE_ENTER_NUMBER = "message.enter.number";
|
||||||
@@ -167,6 +170,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private final NullValidator nullValidator = new NullValidator(null, false);
|
private final NullValidator nullValidator = new NullValidator(null, false);
|
||||||
|
|
||||||
|
@SuppressWarnings("squid:S00107")
|
||||||
AddUpdateRolloutWindowLayout(final RolloutManagement rolloutManagement, final TargetManagement targetManagement,
|
AddUpdateRolloutWindowLayout(final RolloutManagement rolloutManagement, final TargetManagement targetManagement,
|
||||||
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
final UINotification uiNotification, final UiProperties uiProperties, final EntityFactory entityFactory,
|
||||||
final VaadinMessageSource i18n, final UIEventBus eventBus,
|
final VaadinMessageSource i18n, final UIEventBus eventBus,
|
||||||
@@ -339,7 +343,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private int getErrorThresholdPercentage(final int amountGroup) {
|
private int getErrorThresholdPercentage(final int amountGroup) {
|
||||||
int errorThresoldPercent = Integer.parseInt(errorThreshold.getValue());
|
int errorThresoldPercent = Integer.parseInt(errorThreshold.getValue());
|
||||||
if (errorThresholdOptionGroup.getValue().equals(ERRORTHRESOLDOPTIONS.COUNT.getValue())) {
|
if (errorThresholdOptionGroup.getValue().equals(ERROR_THRESHOLD_OPTIONS.COUNT.getValue())) {
|
||||||
final int groupSize = (int) Math.ceil((double) totalTargetsCount / (double) amountGroup);
|
final int groupSize = (int) Math.ceil((double) totalTargetsCount / (double) amountGroup);
|
||||||
final int erroThresoldCount = Integer.parseInt(errorThreshold.getValue());
|
final int erroThresoldCount = Integer.parseInt(errorThreshold.getValue());
|
||||||
errorThresoldPercent = (int) Math.ceil(((float) erroThresoldCount / (float) groupSize) * 100);
|
errorThresoldPercent = (int) Math.ceil(((float) erroThresoldCount / (float) groupSize) * 100);
|
||||||
@@ -626,7 +630,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
|
|
||||||
private OptionGroup createErrorThresholdOptionGroup() {
|
private OptionGroup createErrorThresholdOptionGroup() {
|
||||||
final OptionGroup errorThresoldOptions = new OptionGroup();
|
final OptionGroup errorThresoldOptions = new OptionGroup();
|
||||||
for (final ERRORTHRESOLDOPTIONS option : ERRORTHRESOLDOPTIONS.values()) {
|
for (final ERROR_THRESHOLD_OPTIONS option : ERROR_THRESHOLD_OPTIONS.values()) {
|
||||||
errorThresoldOptions.addItem(option.getValue());
|
errorThresoldOptions.addItem(option.getValue());
|
||||||
}
|
}
|
||||||
errorThresoldOptions.setId(UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_OPTION_ID);
|
errorThresoldOptions.setId(UIComponentIdProvider.ROLLOUT_ERROR_THRESOLD_OPTION_ID);
|
||||||
@@ -640,8 +644,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
private void listenerOnErrorThresoldOptionChange(final ValueChangeEvent event) {
|
private void listenerOnErrorThresoldOptionChange(final ValueChangeEvent event) {
|
||||||
errorThreshold.clear();
|
errorThreshold.clear();
|
||||||
errorThreshold.removeAllValidators();
|
errorThreshold.removeAllValidators();
|
||||||
if (event.getProperty().getValue().equals(ERRORTHRESOLDOPTIONS.COUNT.getValue())) {
|
if (event.getProperty().getValue().equals(ERROR_THRESHOLD_OPTIONS.COUNT.getValue())) {
|
||||||
errorThreshold.addValidator(new ErrorThresoldOptionValidator());
|
errorThreshold.addValidator(new ErrorThresholdOptionValidator());
|
||||||
} else {
|
} else {
|
||||||
errorThreshold.addValidator(new ThresholdFieldValidator());
|
errorThreshold.addValidator(new ThresholdFieldValidator());
|
||||||
}
|
}
|
||||||
@@ -732,6 +736,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
groupsLegendLayout.populateTotalTargets(totalTargetsCount);
|
groupsLegendLayout.populateTotalTargets(totalTargetsCount);
|
||||||
defineGroupsLayout.setTargetFilter(filterQueryString);
|
defineGroupsLayout.setTargetFilter(filterQueryString);
|
||||||
}
|
}
|
||||||
|
noOfGroups.markAsDirty();
|
||||||
onGroupNumberChange(event);
|
onGroupNumberChange(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,7 +776,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setDefaultSaveStartGroupOption() {
|
private void setDefaultSaveStartGroupOption() {
|
||||||
errorThresholdOptionGroup.setValue(ERRORTHRESOLDOPTIONS.PERCENT.getValue());
|
errorThresholdOptionGroup.setValue(ERROR_THRESHOLD_OPTIONS.PERCENT.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TextArea createDescription() {
|
private static TextArea createDescription() {
|
||||||
@@ -802,6 +807,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
final TextField noOfGroupsField = createIntegerTextField("prompt.number.of.groups",
|
final TextField noOfGroupsField = createIntegerTextField("prompt.number.of.groups",
|
||||||
UIComponentIdProvider.ROLLOUT_NO_OF_GROUPS_ID);
|
UIComponentIdProvider.ROLLOUT_NO_OF_GROUPS_ID);
|
||||||
noOfGroupsField.addValidator(new GroupNumberValidator());
|
noOfGroupsField.addValidator(new GroupNumberValidator());
|
||||||
|
noOfGroupsField.addValidator(new GroupSizeValidator());
|
||||||
noOfGroupsField.setMaxLength(3);
|
noOfGroupsField.setMaxLength(3);
|
||||||
noOfGroupsField.addValueChangeListener(this::onGroupNumberChange);
|
noOfGroupsField.addValueChangeListener(this::onGroupNumberChange);
|
||||||
return noOfGroupsField;
|
return noOfGroupsField;
|
||||||
@@ -850,8 +856,8 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
return rolloutNameField;
|
return rolloutNameField;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorThresoldOptionValidator implements Validator {
|
class ErrorThresholdOptionValidator implements Validator {
|
||||||
private static final long serialVersionUID = 9049939751976326550L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(final Object value) {
|
public void validate(final Object value) {
|
||||||
@@ -879,6 +885,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TargetExistsValidator implements Validator {
|
class TargetExistsValidator implements Validator {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -891,7 +898,7 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ThresholdFieldValidator implements Validator {
|
class ThresholdFieldValidator implements Validator {
|
||||||
private static final long serialVersionUID = 9049939751976326550L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(final Object value) {
|
public void validate(final Object value) {
|
||||||
@@ -903,13 +910,29 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GroupNumberValidator implements Validator {
|
class GroupNumberValidator implements Validator {
|
||||||
private static final long serialVersionUID = 9043919751971326521L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validate(final Object value) {
|
public void validate(final Object value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
new IntegerRangeValidator(i18n.getMessage(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 1, 500), 1, 500)
|
final int maxGroups = quotaManagement.getMaxRolloutGroupsPerRollout();
|
||||||
.validate(Integer.valueOf(value.toString()));
|
new IntegerRangeValidator(i18n.getMessage(MESSAGE_ROLLOUT_FIELD_VALUE_RANGE, 1, maxGroups), 1,
|
||||||
|
maxGroups).validate(Integer.valueOf(value.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupSizeValidator implements Validator {
|
||||||
|
private static final long serialVersionUID = 0L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate(final Object value) {
|
||||||
|
if (value != null && totalTargetsCount != null) {
|
||||||
|
final int maxGroupSize = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
if (getGroupSize() > maxGroupSize) {
|
||||||
|
final String msg = i18n.getMessage(MESSAGE_ROLLOUT_MAX_GROUP_SIZE_EXCEEDED, maxGroupSize);
|
||||||
|
throw new InvalidValueException(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1020,12 +1043,15 @@ public class AddUpdateRolloutWindowLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ERRORTHRESOLDOPTIONS {
|
private enum ERROR_THRESHOLD_OPTIONS {
|
||||||
PERCENT("%"), COUNT("Count");
|
|
||||||
|
|
||||||
String value;
|
PERCENT("%"),
|
||||||
|
|
||||||
ERRORTHRESOLDOPTIONS(final String val) {
|
COUNT("Count");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
ERROR_THRESHOLD_OPTIONS(final String val) {
|
||||||
value = val;
|
value = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ import com.vaadin.ui.UI;
|
|||||||
*/
|
*/
|
||||||
public class DefineGroupsLayout extends GridLayout {
|
public class DefineGroupsLayout extends GridLayout {
|
||||||
|
|
||||||
private static final long serialVersionUID = 2939193468001472916L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private static final String MESSAGE_ROLLOUT_MAX_GROUP_SIZE_EXCEEDED = "message.rollout.max.group.size.exceeded.advanced";
|
||||||
|
|
||||||
private final VaadinMessageSource i18n;
|
private final VaadinMessageSource i18n;
|
||||||
|
|
||||||
@@ -267,7 +269,7 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
validateRemainingTargets();
|
validateRemainingTargets();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
resetRemainingTargetsError();
|
resetErrors();
|
||||||
setValidationStatus(ValidationStatus.INVALID);
|
setValidationStatus(ValidationStatus.INVALID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,12 +281,12 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetRemainingTargetsError() {
|
private void resetErrors() {
|
||||||
groupRows.forEach(GroupRow::hideLastGroupError);
|
groupRows.forEach(GroupRow::resetError);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateRemainingTargets() {
|
private void validateRemainingTargets() {
|
||||||
resetRemainingTargetsError();
|
resetErrors();
|
||||||
if (targetFilter == null) {
|
if (targetFilter == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -318,16 +320,33 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
}
|
}
|
||||||
groupsValidation = validation;
|
groupsValidation = validation;
|
||||||
|
|
||||||
final GroupRow lastRow = groupRows.get(groupRows.size() - 1);
|
final int lastIdx = groupRows.size() - 1;
|
||||||
|
final GroupRow lastRow = groupRows.get(lastIdx);
|
||||||
if (groupsValidation != null && groupsValidation.isValid() && validationStatus != ValidationStatus.INVALID) {
|
if (groupsValidation != null && groupsValidation.isValid() && validationStatus != ValidationStatus.INVALID) {
|
||||||
lastRow.hideLastGroupError();
|
lastRow.resetError();
|
||||||
setValidationStatus(ValidationStatus.VALID);
|
setValidationStatus(ValidationStatus.VALID);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
lastRow.markWithLastGroupError();
|
lastRow.setError(i18n.getMessage("message.rollout.remaining.targets.error"));
|
||||||
setValidationStatus(ValidationStatus.INVALID);
|
setValidationStatus(ValidationStatus.INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate the single groups
|
||||||
|
final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup();
|
||||||
|
final boolean hasRemainingTargetsError = validationStatus == ValidationStatus.INVALID;
|
||||||
|
for (int i = 0; i < groupRows.size(); ++i) {
|
||||||
|
final GroupRow row = groupRows.get(i);
|
||||||
|
// do not mask the 'remaining targets' error
|
||||||
|
if (hasRemainingTargetsError && row.equals(lastRow)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
row.resetError();
|
||||||
|
final Long count = groupsValidation.getTargetsPerGroup().get(i);
|
||||||
|
if (count != null && count > maxTargets) {
|
||||||
|
row.setError(i18n.getMessage(MESSAGE_ROLLOUT_MAX_GROUP_SIZE_EXCEEDED, maxTargets));
|
||||||
|
setValidationStatus(ValidationStatus.INVALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<RolloutGroupCreate> getGroupsFromRows() {
|
private List<RolloutGroupCreate> getGroupsFromRows() {
|
||||||
@@ -389,13 +408,9 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
groupName = createTextField("textfield.name", UIComponentIdProvider.ROLLOUT_GROUP_LIST_GRID_ID);
|
groupName = createTextField("textfield.name", UIComponentIdProvider.ROLLOUT_GROUP_LIST_GRID_ID);
|
||||||
groupName.setValue(i18n.getMessage("textfield.rollout.group.default.name", groupsCount));
|
groupName.setValue(i18n.getMessage("textfield.rollout.group.default.name", groupsCount));
|
||||||
groupName.setStyleName("rollout-group-name");
|
groupName.setStyleName("rollout-group-name");
|
||||||
groupName.addValueChangeListener(
|
groupName.addValueChangeListener(event -> valueChanged());
|
||||||
|
|
||||||
event -> valueChanged());
|
targetFilterQueryCombo = createTargetFilterQueryCombo();
|
||||||
|
|
||||||
targetFilterQueryCombo =
|
|
||||||
|
|
||||||
createTargetFilterQueryCombo();
|
|
||||||
|
|
||||||
populateTargetFilterQuery();
|
populateTargetFilterQuery();
|
||||||
targetFilterQueryCombo.addValueChangeListener(event -> valueChanged());
|
targetFilterQueryCombo.addValueChangeListener(event -> valueChanged());
|
||||||
@@ -617,18 +632,16 @@ public class DefineGroupsLayout extends GridLayout {
|
|||||||
&& triggerThreshold.isValid() && errorThreshold.isValid();
|
&& triggerThreshold.isValid() && errorThreshold.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markWithLastGroupError() {
|
private void setError(final String error) {
|
||||||
targetPercentage
|
targetPercentage.setComponentError(new UserError(error));
|
||||||
.setComponentError(new UserError(i18n.getMessage("message.rollout.remaining.targets.error")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides an error of the row
|
* Hides an error of the row
|
||||||
*/
|
*/
|
||||||
private void hideLastGroupError() {
|
private void resetError() {
|
||||||
targetPercentage.setComponentError(null);
|
targetPercentage.setComponentError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.springframework.data.domain.Sort.Direction;
|
|||||||
* Class to provide the unchanged constants.
|
* Class to provide the unchanged constants.
|
||||||
*/
|
*/
|
||||||
public final class SPUIDefinitions {
|
public final class SPUIDefinitions {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Available locales.
|
* Available locales.
|
||||||
*/
|
*/
|
||||||
@@ -27,10 +28,12 @@ public final class SPUIDefinitions {
|
|||||||
* Default locale.
|
* Default locale.
|
||||||
*/
|
*/
|
||||||
public static final String DEFAULT_LOCALE = "en";
|
public static final String DEFAULT_LOCALE = "en";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locale cookie name.
|
* Locale cookie name.
|
||||||
*/
|
*/
|
||||||
public static final String COOKIE_NAME = "BOSCHSI_UII_LOCALE";
|
public static final String COOKIE_NAME = "BOSCHSI_UII_LOCALE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy query container page size.
|
* Lazy query container page size.
|
||||||
*/
|
*/
|
||||||
@@ -166,10 +169,12 @@ public final class SPUIDefinitions {
|
|||||||
* New Software Module desc field id.
|
* New Software Module desc field id.
|
||||||
*/
|
*/
|
||||||
public static final String NEW_SOFTWARE_TYPE_DESC = "software.type.add.desc";
|
public static final String NEW_SOFTWARE_TYPE_DESC = "software.type.add.desc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide filter by dist type layout button.
|
* Hide filter by dist type layout button.
|
||||||
*/
|
*/
|
||||||
public static final String HIDE_FILTER_DIST_TYPE = "hide.filter.dist.type.layout";
|
public static final String HIDE_FILTER_DIST_TYPE = "hide.filter.dist.type.layout";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New Distribution Type distribution field id.
|
* New Distribution Type distribution field id.
|
||||||
*/
|
*/
|
||||||
@@ -184,18 +189,22 @@ public final class SPUIDefinitions {
|
|||||||
* New Software Module key field id.
|
* New Software Module key field id.
|
||||||
*/
|
*/
|
||||||
public static final String NEW_SOFTWARE_TYPE_KEY = "software.type.add.key";
|
public static final String NEW_SOFTWARE_TYPE_KEY = "software.type.add.key";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New Target tag desc field id.
|
* New Target tag desc field id.
|
||||||
*/
|
*/
|
||||||
public static final String NEW_TARGET_TAG_DESC = "target.tag.add.desc";
|
public static final String NEW_TARGET_TAG_DESC = "target.tag.add.desc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New distribution Type set tag add icon id.
|
* New distribution Type set tag add icon id.
|
||||||
*/
|
*/
|
||||||
public static final String ADD_DISTRIBUTION_TYPE_TAG = "distribution.type.tag.add";
|
public static final String ADD_DISTRIBUTION_TYPE_TAG = "distribution.type.tag.add";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* New software module set type add icon id.
|
* New software module set type add icon id.
|
||||||
*/
|
*/
|
||||||
public static final String ADD_SOFTWARE_MODULE_TYPE = "softwaremodule.type.add";
|
public static final String ADD_SOFTWARE_MODULE_TYPE = "softwaremodule.type.add";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No data.
|
* No data.
|
||||||
*/
|
*/
|
||||||
@@ -220,10 +229,12 @@ public final class SPUIDefinitions {
|
|||||||
* Filter by tag key.
|
* Filter by tag key.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_TAG = "FilterByTag";
|
public static final String FILTER_BY_TAG = "FilterByTag";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by no tag button.
|
* Filter by no tag button.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_NO_TAG = "FilterByNoTag";
|
public static final String FILTER_BY_NO_TAG = "FilterByNoTag";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by target filter query.
|
* Filter by target filter query.
|
||||||
*/
|
*/
|
||||||
@@ -233,34 +244,42 @@ public final class SPUIDefinitions {
|
|||||||
* Filter by distribution key.
|
* Filter by distribution key.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_DISTRIBUTION = "FilterByDistribution";
|
public static final String FILTER_BY_DISTRIBUTION = "FilterByDistribution";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by distributionSet Type.
|
* Filter by distributionSet Type.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_DISTRIBUTION_SET_TYPE = "FilterByDistributionSetType";
|
public static final String FILTER_BY_DISTRIBUTION_SET_TYPE = "FilterByDistributionSetType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Order by pinnedTarget.
|
* Order by pinnedTarget.
|
||||||
*/
|
*/
|
||||||
public static final String ORDER_BY_PINNED_TARGET = "OrderByPinnedTarget";
|
public static final String ORDER_BY_PINNED_TARGET = "OrderByPinnedTarget";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by text key.
|
* Filter by text key.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_BY_TEXT = "FilterByText";
|
public static final String FILTER_BY_TEXT = "FilterByText";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text field style.
|
* Text field style.
|
||||||
*/
|
*/
|
||||||
public static final String TEXT_STYLE = "text-style";
|
public static final String TEXT_STYLE = "text-style";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show actions for a target.
|
* Show actions for a target.
|
||||||
*/
|
*/
|
||||||
public static final String ACTIONS_BY_TARGET = "ActionsByTarget";
|
public static final String ACTIONS_BY_TARGET = "ActionsByTarget";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show action-states for a action.
|
* Show action-states for a action.
|
||||||
*/
|
*/
|
||||||
public static final String ACTIONSTATES_BY_ACTION = "ActionStatesByAction";
|
public static final String ACTIONSTATES_BY_ACTION = "ActionStatesByAction";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show messages for a action-status.
|
* Show messages for a action-status.
|
||||||
*/
|
*/
|
||||||
public static final String MESSAGES_BY_ACTIONSTATUS = "MessagesByActionStatus";
|
public static final String MESSAGES_BY_ACTIONSTATUS = "MessagesByActionStatus";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key for no-message MessageProxy.
|
* Key for no-message MessageProxy.
|
||||||
*/
|
*/
|
||||||
@@ -290,22 +309,27 @@ public final class SPUIDefinitions {
|
|||||||
* Target and distribution column width in save popup window.
|
* Target and distribution column width in save popup window.
|
||||||
*/
|
*/
|
||||||
public static final float TARGET_DISTRIBUTION_COLUMN_WIDTH = 2.8F;
|
public static final float TARGET_DISTRIBUTION_COLUMN_WIDTH = 2.8F;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discard column width in save window popup.
|
* Discard column width in save window popup.
|
||||||
*/
|
*/
|
||||||
public static final float DISCARD_COLUMN_WIDTH = 1.2F;
|
public static final float DISCARD_COLUMN_WIDTH = 1.2F;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target tag name.
|
* Target tag name.
|
||||||
*/
|
*/
|
||||||
public static final String TAG_NAME = "target-tag-name";
|
public static final String TAG_NAME = "target-tag-name";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target tag desc.
|
* Target tag desc.
|
||||||
*/
|
*/
|
||||||
public static final String TAG_DESC = "target-tag-desc";
|
public static final String TAG_DESC = "target-tag-desc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Software type name.
|
* Software type name.
|
||||||
*/
|
*/
|
||||||
public static final String TYPE_NAME = "type-name";
|
public static final String TYPE_NAME = "type-name";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DistributionSet type name.
|
* DistributionSet type name.
|
||||||
*/
|
*/
|
||||||
@@ -320,6 +344,7 @@ public final class SPUIDefinitions {
|
|||||||
* DistributionSet type key.
|
* DistributionSet type key.
|
||||||
*/
|
*/
|
||||||
public static final String DIST_SET_TYPE_KEY = "dist-set-type-key";
|
public static final String DIST_SET_TYPE_KEY = "dist-set-type-key";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Software type desc.
|
* Software type desc.
|
||||||
*/
|
*/
|
||||||
@@ -334,6 +359,7 @@ public final class SPUIDefinitions {
|
|||||||
* Tag combo style.
|
* Tag combo style.
|
||||||
*/
|
*/
|
||||||
public static final String FILTER_TYPE_COMBO_STYLE = "filter-combo-specific-style";
|
public static final String FILTER_TYPE_COMBO_STYLE = "filter-combo-specific-style";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Color label style.
|
* Color label style.
|
||||||
*/
|
*/
|
||||||
@@ -351,6 +377,7 @@ public final class SPUIDefinitions {
|
|||||||
* Target tag button id prefix.
|
* Target tag button id prefix.
|
||||||
*/
|
*/
|
||||||
public static final String TARGET_TAG_ID_PREFIXS = "target.tag.";
|
public static final String TARGET_TAG_ID_PREFIXS = "target.tag.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Distribution tag button id prefix.
|
* Distribution tag button id prefix.
|
||||||
*/
|
*/
|
||||||
@@ -359,7 +386,7 @@ public final class SPUIDefinitions {
|
|||||||
/**
|
/**
|
||||||
* Space.
|
* Space.
|
||||||
*/
|
*/
|
||||||
static final String SPACE = " ";
|
public static final String SPACE = " ";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Distribution tag button id prefix.
|
* Distribution tag button id prefix.
|
||||||
@@ -370,27 +397,33 @@ public final class SPUIDefinitions {
|
|||||||
* DistributionSet Type tag button id prefix.
|
* DistributionSet Type tag button id prefix.
|
||||||
*/
|
*/
|
||||||
public static final String DISTRIBUTION_SET_TYPE_ID_PREFIXS = "dist.set.type.";
|
public static final String DISTRIBUTION_SET_TYPE_ID_PREFIXS = "dist.set.type.";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target last query date format .
|
* Target last query date format .
|
||||||
*/
|
*/
|
||||||
public static final String LAST_QUERY_DATE_FORMAT = "EEE MMM d HH:mm:ss z yyyy";
|
public static final String LAST_QUERY_DATE_FORMAT = "EEE MMM d HH:mm:ss z yyyy";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target last query date format .
|
* Target last query date format .
|
||||||
*/
|
*/
|
||||||
public static final String LAST_QUERY_DATE_FORMAT_SHORT = "MMM d HH:mm z ''yy";
|
public static final String LAST_QUERY_DATE_FORMAT_SHORT = "MMM d HH:mm z ''yy";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item Id used in drop comparisons.
|
* Item Id used in drop comparisons.
|
||||||
*/
|
*/
|
||||||
public static final String ITEMID = "itemId";
|
public static final String ITEMID = "itemId";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand action history.
|
* Expand action history.
|
||||||
*/
|
*/
|
||||||
public static final String EXPAND_ACTION_HISTORY = "expand.action.history";
|
public static final String EXPAND_ACTION_HISTORY = "expand.action.history";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by distribution key.
|
* Filter by distribution key.
|
||||||
*/
|
*/
|
||||||
public static final String ORDER_BY_DISTRIBUTION = "OrderByDistribution";
|
public static final String ORDER_BY_DISTRIBUTION = "OrderByDistribution";
|
||||||
|
|
||||||
|
|
||||||
/** Artifact upload related entries - start **/
|
/** Artifact upload related entries - start **/
|
||||||
/**
|
/**
|
||||||
* Artifact details by Base software module id.
|
* Artifact details by Base software module id.
|
||||||
@@ -408,15 +441,15 @@ public final class SPUIDefinitions {
|
|||||||
* automatically and also horizontal scroll bars get displayed. Used for
|
* automatically and also horizontal scroll bars get displayed. Used for
|
||||||
* Responsive UI.
|
* Responsive UI.
|
||||||
*/
|
*/
|
||||||
static final int REQ_MIN_UPLOAD_BROWSER_WIDTH = 1250;
|
public static final int REQ_MIN_UPLOAD_BROWSER_WIDTH = 1250;
|
||||||
|
|
||||||
public static final int MIN_UPLOAD_CONFIRMATION_POPUP_WIDTH = 1000;
|
public static final int MIN_UPLOAD_CONFIRMATION_POPUP_WIDTH = 1000;
|
||||||
|
|
||||||
public static final int MIN_UPLOAD_CONFIRMATION_POPUP_HEIGHT = 310;
|
public static final int MIN_UPLOAD_CONFIRMATION_POPUP_HEIGHT = 310;
|
||||||
|
|
||||||
static final int MAX_UPLOAD_CONFIRMATION_POPUP_WIDTH = 1050;
|
public static final int MAX_UPLOAD_CONFIRMATION_POPUP_WIDTH = 1050;
|
||||||
|
|
||||||
static final int MAX_UPLOAD_CONFIRMATION_POPUP_HEIGHT = 360;
|
public static final int MAX_UPLOAD_CONFIRMATION_POPUP_HEIGHT = 360;
|
||||||
|
|
||||||
/** Artifact upload related entries - end. **/
|
/** Artifact upload related entries - end. **/
|
||||||
|
|
||||||
|
|||||||
@@ -566,6 +566,8 @@ message.correct.invalid.value = Please correct invalid values
|
|||||||
message.enter.number = Please enter number
|
message.enter.number = Please enter number
|
||||||
message.rollout.field.value.range = Value should be in range {0} to {1}
|
message.rollout.field.value.range = Value should be in range {0} to {1}
|
||||||
message.rollout.filter.target.exists = The selected target filter does not match any existing target
|
message.rollout.filter.target.exists = The selected target filter does not match any existing target
|
||||||
|
message.rollout.max.group.size.exceeded = The maximum group size of {0} targets is exceeded. Please increase the number of groups or select a different target filter
|
||||||
|
message.rollout.max.group.size.exceeded.advanced = The maximum size of {0} targets is exceeded for this group. Please re-distribute the targets across the groups and create further groups if needed
|
||||||
message.rollout.started = Rollout {0} started successfully
|
message.rollout.started = Rollout {0} started successfully
|
||||||
message.rollout.paused = Rollout {0} paused successfully
|
message.rollout.paused = Rollout {0} paused successfully
|
||||||
message.rollout.resumed = Rollout {0} resumed successfully
|
message.rollout.resumed = Rollout {0} resumed successfully
|
||||||
|
|||||||
Reference in New Issue
Block a user