Skip to content

Commit

Permalink
[VBV-2022] [BE] Populate favorite images on startup
Browse files Browse the repository at this point in the history
+ Add method to return all FavorteImage states from json file
+ Add states to ComputeInitialBootService
++ Create condition to not populate favorite images if it is a test case
++ Rewrite tests for FavoriteImagesService so they conform to the initial
   population of the default images
++ Implement test case for the initial population of favorite images
+++ Fix checkstyle issues
+++ Generate default images self link based on name and registry
+++ Remove unneeded tests

Change-Id: I8bf71aeaa1dd075d0fc5b832393fd92c74de577c
Reviewed-on: https://bellevue-ci.eng.vmware.com:8080/35885
Upgrade-Verified: jenkins <[email protected]>
Closures-Verified: jenkins <[email protected]>
Bellevue-Verified: jenkins <[email protected]>
CS-Verified: jenkins <[email protected]>
Reviewed-by: Georgi Muleshkov <[email protected]>
  • Loading branch information
AlekBoninski committed Jun 22, 2018
1 parent d3d10de commit 21cb8f4
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public interface ManagementUriParts {
String INSTANCE_TYPE_PROFILES = CONFIG + "/instance-types";
String MIGRATION = CONFIG + "/migration";
String UNIQUE_PROPERTIES = CONFIG + "/unique-properties";
String FAVORITE_IMAGES_FLAG = CONFIG + "/should-populate-favorites";

String COMPOSITE_DESCRIPTION_UPGRADE_TRANSFORM_PATH = UPGRADE_TRANSFORM_PREFIX
+ "/composite-descriptions";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ protected void initInstances(Operation post, boolean checkIfExists, boolean self
ServiceDocument... states) {
if (states == null || states.length == 0) {
post.complete();
logInfo("Finish initial boot service: %s", getSelfLink());
if (selfDelete) {
logInfo("Stopping initial boot service: %s", getSelfLink());
sendRequest(Operation.createDelete(getUri()));
}
return;
}
final AtomicInteger countDown = new AtomicInteger(states.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@
import com.vmware.admiral.compute.container.HostVolumeListDataCollection;
import com.vmware.admiral.compute.container.SystemContainerDescriptions;
import com.vmware.admiral.compute.kubernetes.KubernetesEntityDataCollection;
import com.vmware.admiral.image.service.FavoriteImagePopulateFlagService;
import com.vmware.admiral.image.service.FavoriteImagePopulateFlagService.FavoriteImagePopulateFlag;
import com.vmware.admiral.image.service.FavoriteImagesService;
import com.vmware.admiral.image.service.FavoriteImagesService.FavoriteImage;
import com.vmware.admiral.service.common.AbstractInitialBootService;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceDocument;
import com.vmware.xenon.common.UriUtils;

/**
* Initial boot service for creating system default documents for the common module.
Expand All @@ -39,11 +44,40 @@ public void handlePost(Operation post) {
SystemContainerDescriptions.buildCoreAgentContainerDescription());

ArrayList<ServiceDocument> states = new ArrayList<>();
ArrayList<FavoriteImage> defaultFavoriteImageStates = new ArrayList<>();

/**
* Operation to retrieve the flag which tells whether or not to add the default
* favorite images. In case the images should not be added, initInstances is called
* to complete the deletion of the ComputeInitialBootService.
*/
Operation populateDefaultFavoriteImages = Operation
.createGet(UriUtils.buildUri(getHost(),
FavoriteImagePopulateFlagService.FAVORITE_IMAGE_POPULATE_FLAG_LINK))
.setReferer(getSelfLink())
.addPragmaDirective(Operation.PRAGMA_DIRECTIVE_QUEUE_FOR_SERVICE_AVAILABILITY)
.setCompletion((o, e) -> {
if (e != null) {
logInfo("Could not retrieve shouldPopulate images flag.");
} else {
FavoriteImagePopulateFlag flag = o.hasBody() ? o.getBody(FavoriteImagePopulateFlag.class) : null;
if (flag != null && flag.shouldPopulate) {
//Add the default favorite image states to the list.
defaultFavoriteImageStates.addAll(FavoriteImagesService.buildDefaultFavoriteImages(getHost()));
flag.shouldPopulate = Boolean.FALSE;
sendRequest(updateShouldPopulateFlagState(flag));
}
}
initInstances(post, defaultFavoriteImageStates.toArray(
new ServiceDocument[defaultFavoriteImageStates.size()]));
});

states.add(ContainerHostDataCollectionService.buildDefaultStateInstance());
states.add(KubernetesEntityDataCollection.buildDefaultStateInstance());
states.add(HostContainerListDataCollection.buildDefaultStateInstance());
states.add(HostNetworkListDataCollection.buildDefaultStateInstance());
states.add(HostVolumeListDataCollection.buildDefaultStateInstance());
states.add(FavoriteImagePopulateFlagService.buildDefaultStateInstance());

if (DeploymentProfileConfig.getInstance().isTest()) {
states.add(GroupResourcePlacementService.buildDefaultResourcePool());
Expand All @@ -52,6 +86,26 @@ public void handlePost(Operation post) {
}
}

initInstances(post, states.toArray(new ServiceDocument[states.size()]));
/**
* Initialize default instances without deleting the initial boot service.
*/
initInstances(post, true, false, states.toArray(new ServiceDocument[states.size()]));
/**
* Check whether adding the default favorite images is needed and do so if yes.
*/
sendRequest(populateDefaultFavoriteImages);
}

/**
* Updates the state of the shouldPopulate flag.
*
* @param flagState The new state of the flag.
* @return The update operation to be invoked.
*/
private Operation updateShouldPopulateFlagState(FavoriteImagePopulateFlag flagState) {
return Operation.createPut(UriUtils.buildUri(getHost(),flagState.documentSelfLink))
.setReferer(getSelfLink())
.setBody(flagState);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
import com.vmware.admiral.compute.pks.PKSEndpointService;
import com.vmware.admiral.compute.util.DanglingDescriptionsCleanupService;
import com.vmware.admiral.image.service.FavoriteImageFactoryService;
import com.vmware.admiral.image.service.FavoriteImagePopulateFlagService;
import com.vmware.admiral.image.service.FavoriteImagesService;
import com.vmware.photon.controller.model.resources.ComputeDescriptionService;
import com.vmware.photon.controller.model.resources.ComputeService;
Expand Down Expand Up @@ -148,7 +149,8 @@ public static void startServices(ServiceHost host, boolean startMockContainerHos
ReplicationControllerService.class,
ReplicaSetService.class,
PKSEndpointService.class,
FavoriteImagesService.class);
FavoriteImagesService.class,
FavoriteImagePopulateFlagService.class);

startServices(host, ContainerHostService.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,6 @@ public FavoriteImageFactoryService() {
super(FavoriteImage.class);
}

/**
* Thrown when the image which is to be added to favorites is already added.
*/
public class FavoriteImageAlreadyExistsException extends Exception {
public FavoriteImageAlreadyExistsException(String message) {
super(message);
}
}

/**
* Thrown when the registry an image belongs to is either not global or disabled,
* or both.
Expand Down Expand Up @@ -103,6 +94,12 @@ private void completeOrFailOperationForImage(Operation post) {

queryTask.querySpec.query.addBooleanClause(nameClause);
queryTask.querySpec.query.addBooleanClause(registryClause);

if (imageToFavorite.tenantLinks != null && !imageToFavorite.tenantLinks.isEmpty()) {
Query tenantLinkClause = QueryUtil.addTenantClause(imageToFavorite.tenantLinks);
queryTask.querySpec.query.addBooleanClause(tenantLinkClause);
}

QueryUtil.addExpandOption(queryTask);

List<FavoriteImage> existingFavorites = new LinkedList<>();
Expand All @@ -116,9 +113,10 @@ private void completeOrFailOperationForImage(Operation post) {
if (existingFavorites.isEmpty()) {
completeOrFailOperationForRegistry(post, imageToFavorite.registry);
} else {
post.setStatusCode(Operation.STATUS_CODE_BAD_REQUEST);
post.fail(new FavoriteImageAlreadyExistsException("Image " +
"already exists as favorite"));
//If the image is already added, add the existing image state to the POST body
post.setStatusCode(Operation.STATUS_CODE_NOT_MODIFIED);
post.setBody(existingFavorites.get(0));
post.complete();
}
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018 VMware, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product may include a number of subcomponents with separate copyright notices
* and license terms. Your use of these subcomponents is subject to the terms and
* conditions of the subcomponent's license, as noted in the LICENSE file.
*/

package com.vmware.admiral.image.service;

import com.vmware.admiral.common.ManagementUriParts;
import com.vmware.xenon.common.LocalizableValidationException;
import com.vmware.xenon.common.Operation;
import com.vmware.xenon.common.ServiceDocument;
import com.vmware.xenon.common.StatefulService;
import com.vmware.xenon.common.UriUtils;

public class FavoriteImagePopulateFlagService extends StatefulService {
public static final String FACTORY_LINK = ManagementUriParts.FAVORITE_IMAGES_FLAG;
public static final String FAVORITE_IMAGE_POPULATE_FLAG_ID = "flag";
public static final String FAVORITE_IMAGE_POPULATE_FLAG_LINK = UriUtils
.buildUriPath(FACTORY_LINK, FAVORITE_IMAGE_POPULATE_FLAG_ID);

public FavoriteImagePopulateFlagService() {
super(FavoriteImagePopulateFlag.class);
super.toggleOption(ServiceOption.PERSISTENCE, true);
super.toggleOption(ServiceOption.REPLICATION, true);
super.toggleOption(ServiceOption.OWNER_SELECTION, true);
super.toggleOption(ServiceOption.IDEMPOTENT_POST, true);
}

public static FavoriteImagePopulateFlag buildDefaultStateInstance() {
FavoriteImagePopulateFlag state = new FavoriteImagePopulateFlag();
state.documentSelfLink = FAVORITE_IMAGE_POPULATE_FLAG_LINK;
state.shouldPopulate = Boolean.TRUE;
state.shouldPopulateInEmbedded = Boolean.TRUE;
return state;
}

public static class FavoriteImagePopulateFlag extends ServiceDocument {
public Boolean shouldPopulate;
public Boolean shouldPopulateInEmbedded;
}

@Override
public void handlePost(Operation post) {
if (!post.hasBody()) {
post.fail(new IllegalArgumentException("body is required"));
return;
}

FavoriteImagePopulateFlag initState = post.getBody(FavoriteImagePopulateFlag.class);
if (initState.documentSelfLink == null
|| !initState.documentSelfLink
.endsWith(FAVORITE_IMAGE_POPULATE_FLAG_ID)) {
post.fail(new LocalizableValidationException(
"Only one instance of favorite image populate shouldPopulate can be started",
"compute.should-populte-flag.single"));
return;
}

post.setBody(initState).complete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,68 @@

package com.vmware.admiral.image.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.logging.Level;

import com.google.gson.JsonSyntaxException;

import com.vmware.admiral.common.ManagementUriParts;
import com.vmware.admiral.common.util.FileUtil;
import com.vmware.admiral.service.common.MultiTenantDocument;
import com.vmware.xenon.common.ServiceHost;
import com.vmware.xenon.common.StatefulService;
import com.vmware.xenon.common.UriUtils;
import com.vmware.xenon.common.Utils;

public class FavoriteImagesService extends StatefulService {
public static final String FACTORY_LINK = ManagementUriParts.FAVORITE_IMAGES;
private static final String POPULAR_IMAGES_FILE = "/popular-images.json";

public FavoriteImagesService() {
super(FavoriteImage.class);
super.toggleOption(ServiceOption.PERSISTENCE, true);
super.toggleOption(ServiceOption.REPLICATION, true);
super.toggleOption(ServiceOption.OWNER_SELECTION, true);
super.toggleOption(ServiceOption.INSTRUMENTATION, true);
super.toggleOption(ServiceOption.IDEMPOTENT_POST, true);
}

public static List<FavoriteImage> buildDefaultFavoriteImages(ServiceHost host) {
List<FavoriteImage> images = new ArrayList<>();
try {
List jsonImages = Utils.fromJson(FileUtil.getClasspathResourceAsString(POPULAR_IMAGES_FILE), List.class);

host.log(Level.INFO, "Default favorite images loaded.");

Function<FavoriteImage, String> createSelfLink = state -> {
return UriUtils.buildUriPath(FavoriteImagesService.FACTORY_LINK,
new StringBuilder().append(state.registry.replaceFirst("https?://", "")
.replaceAll("\\.", "-"))
.append('-')
.append(state.name.replaceAll("/", "-")
.replaceAll("\\.", "-"))
.toString());
};

jsonImages.forEach(i -> {
Map<String, String> imgObj = (Map<String, String>) i;
FavoriteImage state = new FavoriteImage();
state.name = imgObj.get(FavoriteImage.FIELD_NAME_NAME);
state.description = imgObj.get(FavoriteImage.FIELD_NAME_DESCRIPTION);
state.registry = imgObj.get(FavoriteImage.FIELD_NAME_REGISTRY);
state.documentSelfLink = createSelfLink.apply(state);
images.add(state);
});
} catch (NullPointerException | JsonSyntaxException e) {
host.log(Level.WARNING, "Unable to load default favorite images. " +
"Either the file is missing or it is malformed");
}
return images;
}

public static class FavoriteImage extends MultiTenantDocument {
Expand All @@ -52,9 +99,12 @@ public boolean equals(Object obj) {
return false;
}
FavoriteImage i = (FavoriteImage) obj;
boolean tenantLinkClause = (i.tenantLinks != null ? i.tenantLinks : Collections.emptyList()).equals(
(this.tenantLinks != null ? this.tenantLinks : Collections.emptyList()));

return i.name.equals(this.name) &&
i.registry.equals(this.registry) &&
i.description.equals(this.description);
tenantLinkClause;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.junit.After;
import org.junit.Test;
Expand All @@ -34,6 +37,10 @@
import com.vmware.admiral.compute.container.SystemContainerDescriptions;
import com.vmware.admiral.compute.kubernetes.KubernetesEntityDataCollection;
import com.vmware.admiral.compute.kubernetes.KubernetesEntityDataCollection.KubernetesEntityDataCollectionState;
import com.vmware.admiral.image.service.FavoriteImagePopulateFlagService;
import com.vmware.admiral.image.service.FavoriteImagePopulateFlagService.FavoriteImagePopulateFlag;
import com.vmware.admiral.image.service.FavoriteImagesService;
import com.vmware.admiral.image.service.FavoriteImagesService.FavoriteImage;
import com.vmware.photon.controller.model.resources.ResourcePoolService.ResourcePoolState;
import com.vmware.xenon.common.TaskState.TaskStage;

Expand Down Expand Up @@ -181,4 +188,21 @@ public void testDefaultResourcePoolServiceCreatedOnStartUp() throws Throwable {
assertEquals(GroupResourcePlacementService.DEFAULT_RESOURCE_POOL_ID, resourcePoolState.id);
}

@Test
public void testDefaultFavoriteImagesCreatedOnStartUp() throws Throwable {
List<FavoriteImage> defaultImages = FavoriteImagesService.buildDefaultFavoriteImages(host);
List<FavoriteImage> favoriteImages = getDocumentsOfType(FavoriteImage.class);

assertEquals(defaultImages.size(), favoriteImages.size());

favoriteImages.forEach(i -> {
assertTrue(defaultImages.contains(i));
});

FavoriteImagePopulateFlag shouldPopulateFlag = getDocument(FavoriteImagePopulateFlag.class,
FavoriteImagePopulateFlagService.FAVORITE_IMAGE_POPULATE_FLAG_LINK);

assertFalse(shouldPopulateFlag.shouldPopulate);
}

}
Loading

0 comments on commit 21cb8f4

Please sign in to comment.