package com.sequenceiq.cloudbreak.cloud.gcp.compute;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import javax.inject.Inject;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Component;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.compute.Compute;
import com.google.api.services.compute.model.Disk;
import com.google.api.services.compute.model.Operation;
import com.sequenceiq.cloudbreak.cloud.context.AuthenticatedContext;
import com.sequenceiq.cloudbreak.cloud.context.CloudContext;
import com.sequenceiq.cloudbreak.cloud.gcp.GcpPlatformParameters.GcpDiskType;
import com.sequenceiq.cloudbreak.cloud.gcp.GcpResourceException;
import com.sequenceiq.cloudbreak.cloud.gcp.context.GcpContext;
import com.sequenceiq.cloudbreak.cloud.gcp.service.GcpResourceNameService;
import com.sequenceiq.cloudbreak.cloud.model.AvailabilityZone;
import com.sequenceiq.cloudbreak.cloud.model.CloudInstance;
import com.sequenceiq.cloudbreak.cloud.model.CloudResource;
import com.sequenceiq.cloudbreak.cloud.model.Group;
import com.sequenceiq.cloudbreak.cloud.model.Image;
import com.sequenceiq.cloudbreak.cloud.model.InstanceTemplate;
import com.sequenceiq.cloudbreak.cloud.model.Location;
import com.sequenceiq.cloudbreak.cloud.model.Volume;
import com.sequenceiq.cloudbreak.common.type.ResourceType;
@Component
public class GcpAttachedDiskResourceBuilder extends AbstractGcpComputeBuilder {
@Inject
@Qualifier("intermediateBuilderExecutor")
private AsyncTaskExecutor intermediateBuilderExecutor;
@Override
public List<CloudResource> create(GcpContext context, long privateId, AuthenticatedContext auth, Group group, Image image) {
List<CloudResource> cloudResources = new ArrayList<>();
CloudInstance instance = group.getReferenceInstanceConfiguration();
InstanceTemplate template = instance.getTemplate();
GcpResourceNameService resourceNameService = getResourceNameService();
String groupName = group.getName();
final CloudContext cloudContext = auth.getCloudContext();
final String stackName = cloudContext.getName();
for (int i = 0; i < template.getVolumes().size(); i++) {
final String resourceName = resourceNameService.resourceName(resourceType(), stackName, groupName, privateId, i);
cloudResources.add(createNamedResource(resourceType(), resourceName));
}
return cloudResources;
}
@Override
public List<CloudResource> build(GcpContext context, long privateId, final AuthenticatedContext auth, Group group, Image image,
List<CloudResource> buildableResource, Map<String, String> tags) throws Exception {
CloudInstance instance = group.getReferenceInstanceConfiguration();
InstanceTemplate template = instance.getTemplate();
Volume volume = template.getVolumes().get(0);
List<CloudResource> resources = new ArrayList<>();
final List<CloudResource> syncedResources = Collections.synchronizedList(resources);
final String projectId = context.getProjectId();
final Location location = context.getLocation();
final Compute compute = context.getCompute();
List<Future<Void>> futures = new ArrayList<>();
for (final CloudResource cloudResource : buildableResource) {
final Disk disk = createDisk(volume, projectId, location.getAvailabilityZone(), cloudResource.getName());
Future<Void> submit = intermediateBuilderExecutor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
Compute.Disks.Insert insDisk = compute.disks().insert(projectId, location.getAvailabilityZone().value(), disk);
try {
Operation operation = insDisk.execute();
syncedResources.add(createOperationAwareCloudResource(cloudResource, operation));
if (operation.getHttpErrorStatusCode() != null) {
throw new GcpResourceException(operation.getHttpErrorMessage(), resourceType(), cloudResource.getName());
}
} catch (GoogleJsonResponseException e) {
throw new GcpResourceException(checkException(e), resourceType(), cloudResource.getName());
}
return null;
}
});
futures.add(submit);
}
for (Future<Void> future : futures) {
future.get();
}
return resources;
}
@Override
public CloudResource delete(GcpContext context, AuthenticatedContext auth, CloudResource resource) throws Exception {
String resourceName = resource.getName();
try {
Operation operation = context.getCompute().disks()
.delete(context.getProjectId(), context.getLocation().getAvailabilityZone().value(), resourceName).execute();
return createOperationAwareCloudResource(resource, operation);
} catch (GoogleJsonResponseException e) {
exceptionHandler(e, resourceName, resourceType());
}
return null;
}
@Override
public ResourceType resourceType() {
return ResourceType.GCP_ATTACHED_DISK;
}
@Override
public int order() {
return 1;
}
private Disk createDisk(Volume volume, String projectId, AvailabilityZone availabilityZone, String resourceName) {
Disk disk = new Disk();
disk.setSizeGb((long) volume.getSize());
disk.setName(resourceName);
disk.setType(GcpDiskType.getUrl(projectId, availabilityZone, volume.getType()));
return disk;
}
}