Skip to content

Commit

Permalink
Allow for default cloud provider to be disabled (use only cloud provi…
Browse files Browse the repository at this point in the history
…ders configured in the database). Handle cases where cloud provider has been removed but images, flavours and security groups are still associated to it. Simplify CloudImage model. Catch all exceptions in cloud provider REST calls.
  • Loading branch information
stuartcaunt committed Dec 5, 2024
1 parent f62d9ce commit 9c7a479
Show file tree
Hide file tree
Showing 16 changed files with 68 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@ConfigMapping(prefix = "cloud", namingStrategy = ConfigMapping.NamingStrategy.VERBATIM)
public interface CloudConfiguration {

Boolean defaultProviderEnabled();

String providerType();

String providerName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ public class CloudImage {
private String id;
private String name;
private Long size;
private String createdAt;

public String getId() {
return id;
Expand All @@ -35,14 +34,6 @@ public void setSize(Long size) {
this.size = size;
}

public String getCreatedAt() {
return createdAt;
}

public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}

@Override
public boolean equals(Object object) {
if (object instanceof CloudImage) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ public void deleteInstance(final String id) throws CloudException {
try {
this.computeEndpointClient.deleteServer(this.identityProvider.authenticate(), id);

} catch (CloudClientException e) {
} catch (Exception e) {
throw new CloudException(format("Could not delete server with id %s and response %s: ", id, e.getMessage()));
}
}
Expand All @@ -158,7 +158,7 @@ public String createInstance(final ServerInput input) throws CloudException {
try {
return this.computeEndpointClient.createServer(this.identityProvider.authenticate(), new ServerRequest(input)).server().id();

} catch (CloudClientException e) {
} catch (Exception e) {
throw new CloudException(format("Could not create server with name %s and response %s ", input.name, e.getMessage()));
}
}
Expand All @@ -169,7 +169,7 @@ public List<String> serverSecurityGroups(final String id) throws CloudException
.map(SecurityGroupsResponse.SecurityGroup::name)
.toList();

} catch (CloudClientException e) {
} catch (Exception e) {
logger.error("Failed to get security groups for server with id {} from OpenStack: {}", id, e.getMessage());
return new ArrayList<>();
}
Expand All @@ -179,7 +179,7 @@ public CloudLimit limits() throws CloudException {
try {
return this.computeEndpointClient.limits(this.identityProvider.authenticate()).limits().absolute();

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud limits from OpenStack: {}", e.getMessage());
return null;
}
Expand All @@ -189,7 +189,7 @@ private void runServerAction(final String id, final InstanceActionRequest action
try {
this.computeEndpointClient.runServerAction(this.identityProvider.authenticate(), id, action);

} catch (CloudClientException e) {
} catch (Exception e) {
throw new CloudException(format("%s for server with id %s: %s: ", errorMessage, id, e.getMessage()));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.ill.visa.cloud.exceptions.CloudException;
import eu.ill.visa.cloud.exceptions.CloudClientException;
import eu.ill.visa.cloud.providers.openstack.OpenStackProviderConfiguration;
import eu.ill.visa.cloud.providers.openstack.domain.AuthenticationToken;
import eu.ill.visa.cloud.providers.openstack.http.IdentityEndpointClient;
import eu.ill.visa.cloud.providers.openstack.http.requests.AuthenticationRequest;
import eu.ill.visa.cloud.providers.openstack.http.responses.AuthenticationResponse;
import eu.ill.visa.cloud.providers.openstack.http.IdentityEndpointClient;
import io.quarkus.rest.client.reactive.QuarkusRestClientBuilder;
import jakarta.ws.rs.core.Response;
import org.slf4j.Logger;
Expand Down Expand Up @@ -60,7 +59,7 @@ public String authenticate(boolean force) throws CloudException {

this.token = new AuthenticationToken(token, expiresAt);

} catch (CloudClientException e) {
} catch (Exception e) {
throw new CloudException(e.getMessage());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import eu.ill.visa.cloud.domain.CloudImage;
import eu.ill.visa.cloud.exceptions.CloudAuthenticationException;
import eu.ill.visa.cloud.exceptions.CloudClientException;
import eu.ill.visa.cloud.exceptions.CloudException;
import eu.ill.visa.cloud.providers.openstack.OpenStackProviderConfiguration;
import eu.ill.visa.cloud.providers.openstack.http.ImageEndpointClient;
Expand Down Expand Up @@ -60,7 +59,7 @@ public List<CloudImage> images() throws CloudException {
try {
return this.imageEndpointClient.images(this.identityProvider.authenticate()).images();

} catch (CloudClientException e) {
} catch (Exception e) {
logger.error("Failed to get cloud images from OpenStack: {}", e.getMessage());
return new ArrayList<>();
}
Expand All @@ -70,7 +69,7 @@ public CloudImage image(final String id) throws CloudException {
try {
return this.imageEndpointClient.image(this.identityProvider.authenticate(), id);

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud image with id {} from OpenStack: {}", id, e.getMessage());
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package eu.ill.visa.cloud.providers.openstack.endpoints;

import eu.ill.visa.cloud.exceptions.CloudAuthenticationException;
import eu.ill.visa.cloud.exceptions.CloudClientException;
import eu.ill.visa.cloud.exceptions.CloudException;
import eu.ill.visa.cloud.providers.openstack.OpenStackProviderConfiguration;
import eu.ill.visa.cloud.providers.openstack.http.NetworkEndpointClient;
Expand Down Expand Up @@ -62,7 +61,7 @@ public List<String> securityGroups() throws CloudException {
.sorted(String::compareToIgnoreCase)
.toList();

} catch (CloudClientException e) {
} catch (Exception e) {
logger.error("Failed to get security groups from OpenStack: {}", e.getMessage());
return new ArrayList<>();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package eu.ill.visa.cloud.providers.openstack.http;


import com.fasterxml.jackson.databind.ObjectMapper;
import eu.ill.visa.cloud.domain.CloudImage;
import eu.ill.visa.cloud.exceptions.CloudAuthenticationException;
import eu.ill.visa.cloud.exceptions.CloudClientException;
import eu.ill.visa.cloud.exceptions.CloudNotFoundException;
import eu.ill.visa.cloud.providers.openstack.converters.CloudImageMixin;
import eu.ill.visa.cloud.providers.openstack.http.responses.ImagesResponse;
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import io.quarkus.rest.client.reactive.jackson.ClientObjectMapper;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
Expand All @@ -30,12 +27,6 @@ public interface ImageEndpointClient {
@Consumes(MediaType.APPLICATION_JSON)
CloudImage image(@HeaderParam(HEADER_X_AUTH_TOKEN) String token, @PathParam("imageId") String imageId);

@ClientObjectMapper
static ObjectMapper objectMapper(ObjectMapper defaultObjectMapper) {
return defaultObjectMapper.copy()
.addMixIn(CloudImage.class, CloudImageMixin.class);
}

@ClientExceptionMapper
static RuntimeException toException(Response response) {
if (response.getStatus() == 401) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import eu.ill.visa.cloud.exceptions.CloudNotFoundException;
import eu.ill.visa.cloud.providers.openstack.http.responses.SecurityGroupsResponse;
import io.quarkus.rest.client.reactive.ClientExceptionMapper;
import io.quarkus.rest.client.reactive.ClientQueryParam;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
Expand All @@ -18,6 +19,7 @@ public interface NetworkEndpointClient {
@Path("/v2.0/security-groups")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ClientQueryParam(name = "fields", value = "name")
SecurityGroupsResponse securityGroups(@HeaderParam(HEADER_X_AUTH_TOKEN) String token);

@ClientExceptionMapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public List<CloudInstanceIdentifier> instanceIdentifiers() throws CloudException
try {
return this.webProviderClient.instanceIdentifiers(this.configuration.getAuthToken());

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud instance identifiers from Web Provider: {}", e.getMessage());
return new ArrayList<>();
}
Expand All @@ -58,7 +58,7 @@ public List<CloudInstance> instances() throws CloudException {
try {
return this.webProviderClient.instances(this.configuration.getAuthToken());

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud instances from Web Provider: {}", e.getMessage());
return null;
}
Expand All @@ -72,7 +72,7 @@ public CloudInstance instance(final String id) throws CloudException {
} catch (CloudNotFoundException e) {
return null;

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud instance with id {} from Web Provider: {}", id, e.getMessage());
return null;
}
Expand Down Expand Up @@ -133,7 +133,7 @@ public CloudInstance createInstance(final String name,
final String id = this.webProviderClient.createInstance(this.configuration.getAuthToken(), cloudInstance).id();
return this.instance(id);

} catch (CloudClientException e) {
} catch (Exception e) {
throw new CloudException(format("Could not create server with name %s and response %s ", name, e.getMessage()));
}
}
Expand All @@ -154,7 +154,7 @@ public CloudImage image(final String id) throws CloudException {
try {
return this.webProviderClient.image(this.configuration.getAuthToken(), id);

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud image with id {} from Web Provider: {}", id, e.getMessage());
return null;
}
Expand All @@ -165,7 +165,7 @@ public List<CloudFlavour> flavors() throws CloudException {
try {
return this.webProviderClient.flavours(this.configuration.getAuthToken());

} catch (CloudClientException e) {
} catch (Exception e) {
logger.error("Failed to get cloud images from Web Provider: {}", e.getMessage());
return new ArrayList<>();
}
Expand All @@ -176,7 +176,7 @@ public CloudFlavour flavor(final String id) throws CloudException {
try {
return this.webProviderClient.flavour(this.configuration.getAuthToken(), id);

} catch (CloudClientException e) {
} catch (Exception e) {
logger.warn("Failed to get cloud image with id {} from Web Provider: {}", id, e.getMessage());
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,28 @@ public class CloudClientGateway {
private static final Logger logger = LoggerFactory.getLogger(CloudClientGateway.class);
private final CloudClientFactory factory = new CloudClientFactory();

private CloudClient defaultCloudClient;
private CloudClient defaultCloudClient = null;
private final Map<Long, CloudClient> secondaryCloudClients = new HashMap<>();

@Inject
public CloudClientGateway(final CloudConfiguration configuration) {
try {
this.defaultCloudClient = factory.getClient(configuration);
this.secondaryCloudClients.put(this.defaultCloudClient.getId(), this.defaultCloudClient);
if (configuration.defaultProviderEnabled()) {
this.defaultCloudClient = factory.getClient(configuration);
this.secondaryCloudClients.put(this.defaultCloudClient.getId(), this.defaultCloudClient);
}

} catch (CloudException e) {
logger.error("Failed to create default Cloud Provider: {}", e.getMessage());
}

}

private CloudClient getDefaultCloudClient() {
return defaultCloudClient;
}

public CloudClient getCloudClient(Long id) {
if (id == null) {
return this.getDefaultCloudClient();
if (this.defaultCloudClient == null) {
logger.error("No default Cloud Provider is configured");
}
return this.defaultCloudClient;
}

CloudClient cloudClient = this.secondaryCloudClients.get(id);
Expand All @@ -62,7 +62,7 @@ public CloudClient addCloudClient(Long providerId, String name, ProviderConfigur

return cloudClient;
} catch (CloudException e) {
logger.error("Failed to create default Cloud Provider: {}", e.getMessage());
logger.error("Failed to add Cloud Provider {}: {}", name, e.getMessage());
}

return null;
Expand Down
1 change: 1 addition & 0 deletions visa-core/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ quarkus:
# level: DEBUG

cloud:
defaultProviderEnabled: ${VISA_CLOUD_DEFAULT_PROVIDER_ENABLED:true}
serverNamePrefix: ${VISA_CLOUD_SERVER_NAME_PREFIX}
providerType: ${VISA_DEFAULT_CLOUD_PROVIDER_TYPE:openstack}
providerName: ${VISA_DEFAULT_CLOUD_PROVIDER_NAME:Default}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import eu.ill.visa.web.graphql.types.CloudClientType;
import eu.ill.visa.web.graphql.types.CloudFlavourType;
import eu.ill.visa.web.graphql.types.FlavourType;
import jakarta.validation.constraints.NotNull;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Source;

import java.util.List;

@GraphQLApi
public class FlavourResolver {

Expand All @@ -20,12 +21,16 @@ public FlavourResolver(final CloudClientService cloudClientService) {
this.cloudClientService = cloudClientService;
}

public @NotNull CloudClientType cloudClient(@Source FlavourType flavour) {
CloudClient cloudClient = this.cloudClientService.getCloudClient(flavour.getCloudId());
if (cloudClient != null) {
return new CloudClientType(cloudClient);
}
return null;
public List<CloudClientType> cloudClient(@Source List<FlavourType> flavours) {
List<CloudClient> cloudClients = this.cloudClientService.getCloudClients(flavours.stream().map(FlavourType::getCloudId).distinct().toList());
return flavours.stream().map(flavour -> {
return cloudClients.stream().filter(cloudClient -> {
if (cloudClient == null) {
return false;
}
return cloudClient.getId() == -1 ? flavour.getCloudId() == null : cloudClient.getId().equals(flavour.getCloudId());
}).findFirst().orElse(null);
}).map(cloudClient -> cloudClient == null ? null : new CloudClientType(cloudClient)).toList();
}

public CloudFlavourType cloudFlavour(@Source FlavourType flavour) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import eu.ill.visa.web.graphql.types.CloudClientType;
import eu.ill.visa.web.graphql.types.CloudImageType;
import eu.ill.visa.web.graphql.types.ImageType;
import jakarta.validation.constraints.NotNull;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Source;

import java.util.List;

@GraphQLApi
public class ImageResolver {

Expand All @@ -21,12 +22,16 @@ public ImageResolver(final CloudClientService cloudClientService) {
}


public @NotNull CloudClientType cloudClient(@Source ImageType image) {
CloudClient cloudClient = this.cloudClientService.getCloudClient(image.getCloudId());
if (cloudClient != null) {
return new CloudClientType(cloudClient);
}
return null;
public List<CloudClientType> cloudClient(@Source List<ImageType> images) {
List<CloudClient> cloudClients = this.cloudClientService.getCloudClients(images.stream().map(ImageType::getCloudId).distinct().toList());
return images.stream().map(image -> {
return cloudClients.stream().filter(cloudClient -> {
if (cloudClient == null) {
return false;
}
return cloudClient.getId() == -1 ? image.getCloudId() == null : cloudClient.getId().equals(image.getCloudId());
}).findFirst().orElse(null);
}).map(cloudClient -> cloudClient == null ? null : new CloudClientType(cloudClient)).toList();
}

public CloudImageType cloudImage(@Source ImageType image) {
Expand Down
Loading

0 comments on commit 9c7a479

Please sign in to comment.