Skip to content

Commit

Permalink
Issue #3534: Tag compute resources according to the tool's metadata (#…
Browse files Browse the repository at this point in the history
…3544)

(cherry picked from commit cb8c9fd)
(cherry picked from commit 6eb95b5)
  • Loading branch information
ekazachkova authored and sidoruka committed Jun 17, 2024
1 parent b95806f commit 74fb0da
Show file tree
Hide file tree
Showing 29 changed files with 698 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2017-2024 EPAM Systems, Inc. (https://www.epam.com/)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.epam.pipeline.entity.metadata;

/**
* Provides tag types for common instance tags that can be applied via
* {@link com.epam.pipeline.manager.preference.SystemPreferences#CLUSTER_INSTANCE_TAGS}
*
* Supported values:
* - tool - Docker image of a tool used for a run
* - run_id - Integer ID of a run
* - owner - Username of a run owner
*/
public enum CommonInstanceTagsType {
tool, run_id, owner
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public interface CloudFacade {
RunInstance scaleUpNode(Long runId, RunInstance instance, Map<String, String> runtimeParameters);
RunInstance scaleUpNode(Long runId, RunInstance instance, Map<String, String> runtimeParameters,
Map<String, String> tags);

RunInstance scaleUpPoolNode(String nodeId, NodePool node);

Expand All @@ -45,9 +47,9 @@ public interface CloudFacade {

boolean isNodeExpired(Long runId);

boolean reassignNode(Long oldId, Long newId);
boolean reassignNode(Long oldId, Long newId, Map<String, String> tags);

boolean reassignPoolNode(String nodeLabel, Long newId);
boolean reassignPoolNode(String nodeLabel, Long newId, Map<String, String> tags);

/**
* Fills in provider related data for running instance associated with run,
Expand Down Expand Up @@ -87,7 +89,7 @@ public interface CloudFacade {
/**
* Creates and attaches new disk by the given request to an instance associated with run.
*/
void attachDisk(Long regionId, Long runId, DiskAttachRequest request);
void attachDisk(Long regionId, Long runId, DiskAttachRequest request, Map<String, String> tags);

/**
* Loads all disks attached to an instance associated with run including os, data and swap disks.
Expand All @@ -103,4 +105,6 @@ public interface CloudFacade {
boolean reassignKubeNode(String previousNodeId, String valueOf);

boolean instanceScalingSupported(Long cloudRegionId);

void deleteInstanceTags(Long regionId, String runId, Set<String> tagNames);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
Expand Down Expand Up @@ -86,9 +87,9 @@ public CloudFacadeImpl(final MessageHelper messageHelper,

@Override
public RunInstance scaleUpNode(final Long runId, final RunInstance instance,
final Map<String, String> runtimeParameters) {
final Map<String, String> runtimeParameters, final Map<String, String> tags) {
final AbstractCloudRegion region = regionManager.loadOrDefault(instance.getCloudRegionId());
return getInstanceService(region).scaleUpNode(region, runId, instance, runtimeParameters);
return getInstanceService(region).scaleUpNode(region, runId, instance, runtimeParameters, tags);
}

@Override
Expand Down Expand Up @@ -130,15 +131,15 @@ public boolean isNodeExpired(final Long runId) {
}

@Override
public boolean reassignNode(final Long oldId, final Long newId) {
public boolean reassignNode(final Long oldId, final Long newId, final Map<String, String> tags) {
final AbstractCloudRegion region = getRegionByRunId(oldId);
return getInstanceService(region).reassignNode(region, oldId, newId);
return getInstanceService(region).reassignNode(region, oldId, newId, tags);
}

@Override
public boolean reassignPoolNode(final String nodeLabel, final Long newId) {
public boolean reassignPoolNode(final String nodeLabel, final Long newId, final Map<String, String> tags) {
final AbstractCloudRegion region = loadRegionFromNodeLabels(nodeLabel);
return getInstanceService(region).reassignPoolNode(region, nodeLabel, newId);
return getInstanceService(region).reassignPoolNode(region, nodeLabel, newId, tags);
}

@Override
Expand Down Expand Up @@ -251,9 +252,10 @@ public double getSpotPrice(final Long regionId, final String instanceType) {
}

@Override
public void attachDisk(final Long regionId, final Long runId, final DiskAttachRequest request) {
public void attachDisk(final Long regionId, final Long runId, final DiskAttachRequest request,
final Map<String, String> tags) {
final AbstractCloudRegion region = regionManager.loadOrDefault(regionId);
getInstanceService(region).attachDisk(region, runId, request);
getInstanceService(region).attachDisk(region, runId, request, tags);
}

@Override
Expand Down Expand Up @@ -297,6 +299,11 @@ public boolean instanceScalingSupported(final Long regionId) {
return region.getProvider() != CloudProvider.LOCAL;
}

public void deleteInstanceTags(final Long regionId, final String runId, final Set<String> tagNames) {
final AbstractCloudRegion region = regionManager.loadOrDefault(regionId);
getInstanceService(region).deleteInstanceTags(region, runId, tagNames);
}

private AbstractCloudRegion getRegionByRunId(final Long runId) {
try {
final PipelineRun run = runCRUDService.loadRunById(runId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

public interface CloudInstanceService<T extends AbstractCloudRegion>
extends CloudAwareService {
Expand All @@ -51,7 +52,8 @@ public interface CloudInstanceService<T extends AbstractCloudRegion>
* @param instance
* @return
*/
RunInstance scaleUpNode(T region, Long runId, RunInstance instance, Map<String, String> runtimeParameters);
RunInstance scaleUpNode(T region, Long runId, RunInstance instance, Map<String, String> runtimeParameters,
Map<String, String> tags);

RunInstance scaleUpPoolNode(T region, String nodeId, NodePool node);

Expand Down Expand Up @@ -132,8 +134,8 @@ public interface CloudInstanceService<T extends AbstractCloudRegion>
* @param newId
* @return {@code true} if operation was successful
*/
boolean reassignNode(T region, Long oldId, Long newId);
boolean reassignPoolNode(T region, String nodeLabel, Long newId);
boolean reassignNode(T region, Long oldId, Long newId, Map<String, String> tags);
boolean reassignPoolNode(T region, String nodeLabel, Long newId, Map<String, String> tags);

/**
* Builds environment variables required for running a container in provided region
Expand Down Expand Up @@ -180,8 +182,9 @@ default boolean isNodeExpired(T region, Long runId, Integer keepAliveMinutes) {
* @param region
* @param runId
* @param request
* @param tags
*/
void attachDisk(T region, Long runId, DiskAttachRequest request);
void attachDisk(T region, Long runId, DiskAttachRequest request, Map<String, String> tags);

/**
* Loads all disks attached to cloud instance.
Expand All @@ -197,4 +200,5 @@ default boolean isNodeExpired(T region, Long runId, Integer keepAliveMinutes) {

InstanceDNSRecord deleteInstanceDNSRecord(T region, InstanceDNSRecord record);

void deleteInstanceTags(T region, String runId, Set<String> tagNames);
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

@Service
Expand Down Expand Up @@ -111,9 +112,10 @@ public AWSInstanceService(final EC2Helper ec2Helper,
public RunInstance scaleUpNode(final AwsRegion region,
final Long runId,
final RunInstance instance,
final Map<String, String> runtimeParameters) {
final Map<String, String> runtimeParameters,
final Map<String, String> tags) {
final String command = buildNodeUpCommand(region, String.valueOf(runId), instance,
Collections.emptyMap(), runtimeParameters);
Collections.emptyMap(), runtimeParameters, tags);
return instanceService.runNodeUpScript(cmdExecutor, runId, instance, command, buildScriptEnvVars(region));
}

Expand All @@ -125,7 +127,7 @@ public RunInstance scaleUpPoolNode(final AwsRegion region,
final Map<String, String> labels = Collections.singletonMap(
KubernetesConstants.NODE_POOL_ID_LABEL, String.valueOf(node.getId()));
final String command = buildNodeUpCommand(region, nodeIdLabel, instance, labels,
Collections.emptyMap());
Collections.emptyMap(), Collections.emptyMap());
return instanceService.runNodeUpScript(cmdExecutor, null, instance, command, buildScriptEnvVars(region));
}

Expand All @@ -142,16 +144,18 @@ public void scaleDownPoolNode(final AwsRegion region, final String nodeLabel) {
}

@Override
public boolean reassignNode(final AwsRegion region, final Long oldId, final Long newId) {
public boolean reassignNode(final AwsRegion region, final Long oldId, final Long newId,
final Map<String, String> tags) {
final String command = commandService.buildNodeReassignCommand(
nodeReassignScript, oldId, newId, getProvider().name());
nodeReassignScript, oldId, newId, getProvider().name(), tags);
return instanceService.runNodeReassignScript(cmdExecutor, command, oldId, newId, buildScriptEnvVars(region));
}

@Override
public boolean reassignPoolNode(final AwsRegion region, final String nodeLabel, final Long newId) {
public boolean reassignPoolNode(final AwsRegion region, final String nodeLabel, final Long newId,
final Map<String, String> tags) {
final String command = commandService.buildNodeReassignCommand(
nodeReassignScript, nodeLabel, String.valueOf(newId), getProvider().name());
nodeReassignScript, nodeLabel, String.valueOf(newId), getProvider().name(), tags);
return instanceService.runNodeReassignScript(cmdExecutor, command, nodeLabel,
String.valueOf(newId), buildScriptEnvVars(region));
}
Expand Down Expand Up @@ -257,9 +261,10 @@ public Optional<InstanceTerminationState> getInstanceTerminationState(final AwsR
}

@Override
public void attachDisk(final AwsRegion region, final Long runId, final DiskAttachRequest request) {
public void attachDisk(final AwsRegion region, final Long runId, final DiskAttachRequest request,
final Map<String, String> tags) {
ec2Helper.createAndAttachVolume(String.valueOf(runId), request.getSize(), region,
region.getKmsKeyArn());
region.getKmsKeyArn(), tags);
}

@Override
Expand Down Expand Up @@ -345,14 +350,21 @@ private InstanceDNSRecord getAbsoluteDNSRecord(final InstanceDNSRecord record, f
}
}

@Override
public void deleteInstanceTags(final AwsRegion region, final String runId, final Set<String> tagNames) {
ec2Helper.deleteInstanceTags(region, runId, tagNames);
}

private String buildNodeUpCommand(final AwsRegion region,
final String nodeLabel,
final RunInstance instance,
final Map<String, String> labels,
final Map<String, String> runtimeParameters) {
final Map<String, String> runtimeParameters,
final Map<String, String> tags) {
final NodeUpCommand.NodeUpCommandBuilder commandBuilder = commandService
.buildNodeUpCommand(nodeUpScript, region, nodeLabel, instance, getProviderName(), runtimeParameters)
.sshKey(region.getSshKeyName());
.sshKey(region.getSshKeyName())
.tags(tags);

if (StringUtils.isNotBlank(region.getKmsKeyId())) {
commandBuilder.encryptionKey(region.getKmsKeyId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@
import com.amazonaws.services.ec2.model.AmazonEC2Exception;
import com.amazonaws.services.ec2.model.AttachVolumeRequest;
import com.amazonaws.services.ec2.model.AvailabilityZone;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.CreateVolumeRequest;
import com.amazonaws.services.ec2.model.DeleteTagsRequest;
import com.amazonaws.services.ec2.model.DeleteVolumeRequest;
import com.amazonaws.services.ec2.model.DescribeInstanceTypesRequest;
import com.amazonaws.services.ec2.model.DescribeInstanceTypesResult;
Expand All @@ -48,6 +50,7 @@
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StateReason;
import com.amazonaws.services.ec2.model.StopInstancesRequest;
import com.amazonaws.services.ec2.model.Tag;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.ec2.model.Volume;
import com.amazonaws.waiters.Waiter;
Expand Down Expand Up @@ -78,12 +81,14 @@

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -329,14 +334,27 @@ public Optional<Instance> findInstance(final String instanceId, final AwsRegion
}

public void createAndAttachVolume(final String runId, final Long size,
final AwsRegion awsRegion, final String kmsKeyArn) {
final AwsRegion awsRegion, final String kmsKeyArn,
final Map<String, String> tags) {
final AmazonEC2 client = getEC2Client(awsRegion);
final Instance instance = getAliveInstance(runId, awsRegion);
final String device = getVacantDeviceName(instance);
final String zone = getAvailabilityZone(instance);
final Volume volume = createVolume(client, size, zone, kmsKeyArn);
tryAttachVolume(client, instance, volume, device);
enableVolumeDeletionOnInstanceTermination(client, instance.getInstanceId(), device);
createTags(client, tags, Collections.singletonList(volume.getVolumeId()));
}

public void deleteInstanceTags(final AwsRegion awsRegion, final String runId, final Set<String> tags) {
final AmazonEC2 client = getEC2Client(awsRegion);
final Instance instance = getAliveInstance(runId, awsRegion);

final List<String> resourcesIds = new ArrayList<>();
resourcesIds.add(instance.getInstanceId());
resourcesIds.addAll(getVolumeIds(instance));

deleteTags(client, tags, resourcesIds);
}

private String getVacantDeviceName(final Instance instance) {
Expand Down Expand Up @@ -490,4 +508,20 @@ private Collection<String> getAllowedNetworks(String awsRegion) {
.map(region -> region.getAllowedNetworks().keySet())
.orElse(Collections.emptySet());
}

private void createTags(final AmazonEC2 client, final Map<String, String> tags, final List<String> resourceIds) {
client.createTags(new CreateTagsRequest()
.withResources(resourceIds)
.withTags(tags.entrySet().stream()
.map(entry -> new Tag(entry.getKey(), entry.getValue()))
.collect(Collectors.toList())));
}

private void deleteTags(final AmazonEC2 client, final Set<String> tags, final List<String> resourceIds) {
client.deleteTags(new DeleteTagsRequest()
.withResources(resourceIds)
.withTags(tags.stream()
.map(Tag::new)
.collect(Collectors.toList())));
}
}
Loading

0 comments on commit 74fb0da

Please sign in to comment.