Feature target metadata (#757)
* Defined the model for target matadata and the corresponding repository layer/management * Added target metadata quotas incl enforcement * Extended Target Mgmt REST API to allow for metadata CRUD operations * Added migration scripts for each database * Added back reference to target metadata in JpaTarget * Added tests for target management, Mgmt REST API, target metadata RSQL, and REST documentation * Updated asciidocs for target rest documentation * Fix Allure imports and annotations * Fix review findings Signed-off-by: Bogdan Bondar <Bogdan.Bondar@bosch-si.com> Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
committed by
Stefan Behl
parent
1cbde47370
commit
0cf4f8e8b9
@@ -136,7 +136,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
"DistributionSetType");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.createMetaData(NOT_EXIST_IDL,
|
||||
Arrays.asList(entityFactory.generateMetadata("123", "123"))), "DistributionSet");
|
||||
Arrays.asList(entityFactory.generateDsMetadata("123", "123"))), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.delete(Arrays.asList(NOT_EXIST_IDL)),
|
||||
"DistributionSet");
|
||||
@@ -167,10 +167,10 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
"DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(NOT_EXIST_IDL,
|
||||
entityFactory.generateMetadata("xxx", "xxx")), "DistributionSet");
|
||||
entityFactory.generateDsMetadata("xxx", "xxx")), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(),
|
||||
entityFactory.generateMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
|
||||
entityFactory.generateDsMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -553,7 +553,7 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// update the DS metadata
|
||||
final JpaDistributionSetMetadata updated = (JpaDistributionSetMetadata) distributionSetManagement
|
||||
.updateMetaData(ds.getId(), entityFactory.generateMetadata(knownKey, knownUpdateValue));
|
||||
.updateMetaData(ds.getId(), entityFactory.generateDsMetadata(knownKey, knownUpdateValue));
|
||||
// we are updating the sw meta data so also modifying the base software
|
||||
// module so opt lock
|
||||
// revision must be three
|
||||
|
||||
@@ -41,13 +41,17 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
|
||||
import org.eclipse.hawkbit.repository.exception.QuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
@@ -73,10 +77,12 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Test
|
||||
@Description("Verifies that management get access react as specified on calls for non existing entities by means "
|
||||
+ "of Optional not present.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) })
|
||||
public void nonExistingEntityAccessReturnsNotPresent() {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
assertThat(targetManagement.getByControllerID(NOT_EXIST_ID)).isNotPresent();
|
||||
assertThat(targetManagement.get(NOT_EXIST_IDL)).isNotPresent();
|
||||
assertThat(targetManagement.getMetaDataByControllerId(target.getControllerId(), NOT_EXIST_ID)).isNotPresent();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,8 +95,10 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()), "Target");
|
||||
() -> targetManagement.assignTag(Collections.singletonList(target.getControllerId()), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.assignTag(Collections.singletonList(NOT_EXIST_ID), tag.getId()),
|
||||
"Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByTag(PAGE, NOT_EXIST_IDL), "TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByRsqlAndTag(PAGE, "name==*", NOT_EXIST_IDL), "TargetTag");
|
||||
@@ -123,16 +131,31 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
() -> targetManagement.findByInstalledDistributionSetAndRsql(PAGE, NOT_EXIST_IDL, "name==*"),
|
||||
"DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement
|
||||
.toggleTagAssignment(Collections.singletonList(target.getControllerId()), NOT_EXIST_ID), "TargetTag");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.toggleTagAssignment(Collections.singletonList(target.getControllerId()), NOT_EXIST_ID),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.toggleTagAssignment(Collections.singletonList(NOT_EXIST_ID), tag.getName()),
|
||||
() -> targetManagement.toggleTagAssignment(Collections.singletonList(NOT_EXIST_ID), tag.getName()),
|
||||
"Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.unAssignTag(NOT_EXIST_ID, tag.getId()), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.unAssignTag(target.getControllerId(), NOT_EXIST_IDL),
|
||||
"TargetTag");
|
||||
verifyThrownExceptionBy(() -> targetManagement.update(entityFactory.target().update(NOT_EXIST_ID)), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> targetManagement.createMetaData(NOT_EXIST_ID,
|
||||
Arrays.asList(entityFactory.generateTargetMetadata("123", "123"))), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.deleteMetaData(target.getControllerId(), NOT_EXIST_ID),
|
||||
"TargetMetadata");
|
||||
verifyThrownExceptionBy(() -> targetManagement.getMetaDataByControllerId(NOT_EXIST_ID, "xxx"), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerId(PAGE, NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findMetaDataByControllerIdAndRsql(PAGE, NOT_EXIST_ID, "name==*"),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.updateMetaData(NOT_EXIST_ID, entityFactory.generateTargetMetadata("xxx", "xxx")),
|
||||
"Target");
|
||||
verifyThrownExceptionBy(() -> targetManagement.updateMetaData(target.getControllerId(),
|
||||
entityFactory.generateTargetMetadata(NOT_EXIST_ID, "xxx")), "TargetMetadata");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,8 +166,9 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
.create(entityFactory.target().create().controllerId("targetWithSecurityToken").securityToken("token"));
|
||||
|
||||
// retrieve security token only with READ_TARGET_SEC_TOKEN permission
|
||||
final String securityTokenWithReadPermission = securityRule.runAs(WithSpringAuthorityRule
|
||||
.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN), createdTarget::getSecurityToken);
|
||||
final String securityTokenWithReadPermission = securityRule.runAs(
|
||||
WithSpringAuthorityRule.withUser("OnlyTargetReadPermission", false, SpPermission.READ_TARGET_SEC_TOKEN),
|
||||
createdTarget::getSecurityToken);
|
||||
|
||||
// retrieve security token as system code execution
|
||||
final String securityTokenAsSystemCode = systemSecurityContext.runAsSystem(createdTarget::getSecurityToken);
|
||||
@@ -889,4 +913,138 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(targetManagement.isControllerAttributesRequested(knownTargetId)).isTrue();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Checks that metadata for a target can be created.")
|
||||
public void createTargetMetadata() {
|
||||
final String knownKey = "targetMetaKnownKey";
|
||||
final String knownValue = "targetMetaKnownValue";
|
||||
|
||||
final Target target = testdataFactory.createTarget("targetIdWithMetadata");
|
||||
final JpaTargetMetadata createdMetadata = insertTargetMetadata(knownKey, target, knownValue);
|
||||
|
||||
assertThat(createdMetadata).isNotNull();
|
||||
assertThat(createdMetadata.getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(createdMetadata.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(createdMetadata.getTarget().getId()).isEqualTo(target.getId());
|
||||
assertThat(createdMetadata.getValue()).isEqualTo(knownValue);
|
||||
}
|
||||
|
||||
private JpaTargetMetadata insertTargetMetadata(final String knownKey, final Target target,
|
||||
final String knownValue) {
|
||||
final JpaTargetMetadata metadata = new JpaTargetMetadata(knownKey, knownValue, target);
|
||||
return (JpaTargetMetadata) targetManagement
|
||||
.createMetaData(target.getControllerId(), Collections.singletonList(metadata)).get(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies the enforcement of the metadata quota per target.")
|
||||
public void createTargetMetadataUntilQuotaIsExceeded() {
|
||||
|
||||
// add meta data one by one
|
||||
final Target target1 = testdataFactory.createTarget("target1");
|
||||
final int maxMetaData = quotaManagement.getMaxMetaDataEntriesPerTarget();
|
||||
for (int i = 0; i < maxMetaData; ++i) {
|
||||
assertThat(insertTargetMetadata("k" + i, target1, "v" + i)).isNotNull();
|
||||
}
|
||||
|
||||
// quota exceeded
|
||||
assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> insertTargetMetadata("k" + maxMetaData, target1, "v" + maxMetaData));
|
||||
|
||||
// add multiple meta data entries at once
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final List<MetaData> metaData2 = new ArrayList<>();
|
||||
for (int i = 0; i < maxMetaData + 1; ++i) {
|
||||
metaData2.add(new JpaTargetMetadata("k" + i, "v" + i, target2));
|
||||
}
|
||||
// verify quota is exceeded
|
||||
assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target2.getControllerId(), metaData2));
|
||||
|
||||
// add some meta data entries
|
||||
final Target target3 = testdataFactory.createTarget("target3");
|
||||
final int firstHalf = Math.round(maxMetaData / 2);
|
||||
for (int i = 0; i < firstHalf; ++i) {
|
||||
insertTargetMetadata("k" + i, target3, "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 JpaTargetMetadata("kk" + i, "vv" + i, target3));
|
||||
}
|
||||
// verify quota is exceeded
|
||||
assertThatExceptionOfType(QuotaExceededException.class)
|
||||
.isThrownBy(() -> targetManagement.createMetaData(target3.getControllerId(), metaData3));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that metadata for a target can be updated.")
|
||||
public void updateTargetMetadata() throws InterruptedException {
|
||||
final String knownKey = "myKnownKey";
|
||||
final String knownValue = "myKnownValue";
|
||||
final String knownUpdateValue = "myNewUpdatedValue";
|
||||
|
||||
// create a target
|
||||
final Target target = testdataFactory.createTarget("target1");
|
||||
// initial opt lock revision must be zero
|
||||
assertThat(target.getOptLockRevision()).isEqualTo(1);
|
||||
|
||||
// create target meta data entry
|
||||
insertTargetMetadata(knownKey, target, knownValue);
|
||||
|
||||
Target changedLockRevisionTarget = targetManagement.get(target.getId()).get();
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(2);
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
// update the target metadata
|
||||
final JpaTargetMetadata updated = (JpaTargetMetadata) targetManagement.updateMetaData(target.getControllerId(),
|
||||
entityFactory.generateTargetMetadata(knownKey, knownUpdateValue));
|
||||
// we are updating the target meta data so also modifying the base
|
||||
// software
|
||||
// module so opt lock
|
||||
// revision must be three
|
||||
changedLockRevisionTarget = targetManagement.get(target.getId()).get();
|
||||
assertThat(changedLockRevisionTarget.getOptLockRevision()).isEqualTo(3);
|
||||
assertThat(changedLockRevisionTarget.getLastModifiedAt()).isGreaterThan(0L);
|
||||
|
||||
// verify updated meta data contains the updated value
|
||||
assertThat(updated).isNotNull();
|
||||
assertThat(updated.getValue()).isEqualTo(knownUpdateValue);
|
||||
assertThat(updated.getId().getKey()).isEqualTo(knownKey);
|
||||
assertThat(updated.getTarget().getControllerId()).isEqualTo(target.getControllerId());
|
||||
assertThat(updated.getTarget().getId()).isEqualTo(target.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given target.")
|
||||
public void findAllTargetMetadataByControllerId() {
|
||||
// create targets
|
||||
final Target target1 = testdataFactory.createTarget("target1");
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
|
||||
for (int index = 0; index < 10; index++) {
|
||||
insertTargetMetadata("key" + index, target1, "value" + index);
|
||||
}
|
||||
|
||||
for (int index = 0; index < 8; index++) {
|
||||
insertTargetMetadata("key" + index, target2, "value" + index);
|
||||
}
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget1 = targetManagement
|
||||
.findMetaDataByControllerId(new PageRequest(0, 100), target1.getControllerId());
|
||||
|
||||
final Page<TargetMetadata> metadataOfTarget2 = targetManagement
|
||||
.findMetaDataByControllerId(new PageRequest(0, 100), target2.getControllerId());
|
||||
|
||||
assertThat(metadataOfTarget1.getNumberOfElements()).isEqualTo(10);
|
||||
assertThat(metadataOfTarget1.getTotalElements()).isEqualTo(10);
|
||||
|
||||
assertThat(metadataOfTarget2.getNumberOfElements()).isEqualTo(8);
|
||||
assertThat(metadataOfTarget2.getTotalElements()).isEqualTo(8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +37,12 @@ public class RSQLDistributionSetFieldTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
DistributionSet ds = testdataFactory.createDistributionSet("DS");
|
||||
ds = distributionSetManagement.update(entityFactory.distributionSet().update(ds.getId()).description("DS"));
|
||||
createDistributionSetMetadata(ds.getId(), entityFactory.generateMetadata("metaKey", "metaValue"));
|
||||
createDistributionSetMetadata(ds.getId(), entityFactory.generateDsMetadata("metaKey", "metaValue"));
|
||||
|
||||
DistributionSet ds2 = testdataFactory.createDistributionSets("NewDS", 3).get(0);
|
||||
|
||||
ds2 = distributionSetManagement.update(entityFactory.distributionSet().update(ds2.getId()).description("DS%"));
|
||||
createDistributionSetMetadata(ds2.getId(), entityFactory.generateMetadata("metaKey", "value"));
|
||||
createDistributionSetMetadata(ds2.getId(), entityFactory.generateDsMetadata("metaKey", "value"));
|
||||
|
||||
final DistributionSetTag targetTag = distributionSetTagManagement
|
||||
.create(entityFactory.tag().create().name("Tag1"));
|
||||
|
||||
@@ -41,13 +41,13 @@ public class RSQLDistributionSetMetadataFieldsTest extends AbstractJpaIntegratio
|
||||
|
||||
final List<MetaData> metadata = new ArrayList<>(5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
metadata.add(entityFactory.generateMetadata("" + i, "" + i));
|
||||
metadata.add(entityFactory.generateDsMetadata("" + i, "" + i));
|
||||
}
|
||||
|
||||
distributionSetManagement.createMetaData(distributionSetId, metadata);
|
||||
|
||||
distributionSetManagement.createMetaData(distributionSetId,
|
||||
Arrays.asList(entityFactory.generateMetadata("emptyValueTest", null)));
|
||||
Arrays.asList(entityFactory.generateDsMetadata("emptyValueTest", null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rsql;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetMetadataFields;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.model.MetaData;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetMetadata;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("RSQL filter target metadata")
|
||||
public class RSQLTargetMetadataFieldsTest extends AbstractJpaIntegrationTest {
|
||||
private String controllerId;
|
||||
|
||||
@Before
|
||||
public void setupBeforeTest() {
|
||||
final Target target = testdataFactory.createTarget("target");
|
||||
controllerId = target.getControllerId();
|
||||
|
||||
final List<MetaData> metadata = new ArrayList<>(5);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
metadata.add(entityFactory.generateTargetMetadata("" + i, "" + i));
|
||||
}
|
||||
|
||||
targetManagement.createMetaData(controllerId, metadata);
|
||||
|
||||
targetManagement.createMetaData(controllerId,
|
||||
Arrays.asList(entityFactory.generateTargetMetadata("emptyValueTest", null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by key")
|
||||
public void testFilterByParameterKey() {
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "!=1", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.KEY.name() + "=out=(1,2)", 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test filter target metadata by value")
|
||||
public void testFilterByParameterValue() {
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==''", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=''", 5);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "==1", 1);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "!=1", 4);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=in=(1,2)", 2);
|
||||
assertRSQLQuery(TargetMetadataFields.VALUE.name() + "=out=(1,2)", 3);
|
||||
}
|
||||
|
||||
private void assertRSQLQuery(final String rsqlParam, final long expectedEntities) {
|
||||
|
||||
final Page<TargetMetadata> findEnitity = targetManagement
|
||||
.findMetaDataByControllerIdAndRsql(new PageRequest(0, 100), controllerId, rsqlParam);
|
||||
final long countAllEntities = findEnitity.getTotalElements();
|
||||
assertThat(findEnitity).isNotNull();
|
||||
assertThat(countAllEntities).isEqualTo(expectedEntities);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user