package org.zalando.stups.fullstop.plugin; import com.amazonaws.regions.Region; import com.amazonaws.services.cloudtrail.processinglibrary.model.CloudTrailEvent; import com.amazonaws.services.cloudtrail.processinglibrary.model.CloudTrailEventData; import com.amazonaws.services.s3.model.ObjectMetadata; import com.google.common.base.Splitter; import com.google.common.collect.Lists; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.zalando.stups.fullstop.events.CloudTrailEventSupport; import org.zalando.stups.fullstop.s3.S3Service; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.file.Paths; import java.util.List; import static java.nio.charset.StandardCharsets.UTF_8; import static org.joda.time.DateTimeZone.UTC; import static org.zalando.stups.fullstop.events.CloudTrailEventSupport.*; /** * @author gkneitschel */ @Component public class SaveSecurityGroupsPlugin extends AbstractFullstopPlugin { public static final String SECURITY_GROUPS = "security-groups-"; public static final String JSON = ".json"; private static final Logger LOG = LoggerFactory.getLogger(SaveSecurityGroupsPlugin.class); private static final String EC2_SOURCE_EVENTS = "ec2.amazonaws.com"; private static final String EVENT_NAME = "RunInstances"; private final S3Service s3Writer; private final SecurityGroupProvider securityGroupProvider; private final String bucketName; @Autowired public SaveSecurityGroupsPlugin(final SecurityGroupProvider securityGroupProvider, final S3Service s3Writer, @Value("${fullstop.instanceData.bucketName}") final String bucketName) { this.securityGroupProvider = securityGroupProvider; this.s3Writer = s3Writer; this.bucketName = bucketName; } @Override public boolean supports(final CloudTrailEvent event) { final CloudTrailEventData cloudTrailEventData = event.getEventData(); final String eventSource = cloudTrailEventData.getEventSource(); final String eventName = cloudTrailEventData.getEventName(); return EC2_SOURCE_EVENTS.equals(eventSource) && EVENT_NAME.equals(eventName); } @Override public void processEvent(final CloudTrailEvent event) { final List<String> securityGroupIds = readSecurityGroupIds(event); final Region region = getRegion(event); final String accountId = getAccountId(event); final List<String> instanceIds = getInstanceIds(event); if (instanceIds.isEmpty()) { LOG.warn("No instanceIds for event : {}, skip processing", CloudTrailEventSupport.getEventId(event)); return; } final String securityGroup = getSecurityGroup(securityGroupIds, region, accountId); if (securityGroup == null) { return; } for (final String instanceId : instanceIds) { final List<String> instanceBuckets = Lists.newArrayList(); final DateTime instanceLaunchTime; try { instanceLaunchTime = new DateTime(getInstanceLaunchTime(event).get(instanceIds.indexOf(instanceId))); } catch (final Exception e) { LOG.warn("No 'launchTime' for event : {}, skip processing", CloudTrailEventSupport.getEventId(event)); return; } final String prefix = PrefixBuilder.build(accountId, region.getName(), instanceLaunchTime); final List<String> s3InstanceObjects = listS3Objects(bucketName, prefix); for (final String s3InstanceObject : s3InstanceObjects) { final String s = Paths.get(s3InstanceObject).getFileName().toString(); if (s.startsWith(instanceId)) { instanceBuckets.add(s); } } if (instanceBuckets.isEmpty()) { continue; } String instanceBucketNameControlElement = null; DateTime instanceBootTimeControlElement = null; for (final String instanceBucket : instanceBuckets) { final List<String> currentBucket = Lists.newArrayList( Splitter.on('-').limit(3).trimResults() .omitEmptyStrings().split(instanceBucket)); final String currentBucketName = currentBucket.get(0) + "-" + currentBucket.get(1); final DateTime currentBucketDate; try { currentBucketDate = new DateTime(currentBucket.get(2), UTC); } catch (final IllegalArgumentException e) { continue; } // TODO we should use absolute values if (instanceBucketNameControlElement != null) { if (instanceLaunchTime.getMillis() - currentBucketDate.getMillis() < instanceLaunchTime.getMillis() - instanceBootTimeControlElement.getMillis()) { instanceBucketNameControlElement = currentBucketName; instanceBootTimeControlElement = currentBucketDate; } } else { instanceBucketNameControlElement = currentBucketName; instanceBootTimeControlElement = currentBucketDate; } } final String result = prefix + instanceBucketNameControlElement + "-" + instanceBootTimeControlElement; writeToS3(securityGroup, result, instanceId); } } public String getSecurityGroup(final List<String> securityGroupIds, final Region region, final String accountId) { return securityGroupProvider.getSecurityGroup(securityGroupIds, region, accountId); } protected void writeToS3(final String content, final String prefix, final String instanceId) { final byte[] bytes = content.getBytes(UTF_8); final InputStream stream = new ByteArrayInputStream(bytes); final ObjectMetadata metadata = new ObjectMetadata(); metadata.setContentLength(bytes.length); final String fileName = instanceId + SECURITY_GROUPS + new DateTime(UTC) + JSON; s3Writer.putObjectToS3(bucketName, fileName, prefix, metadata, stream); } protected List<String> listS3Objects(final String bucketName, final String prefix) { return s3Writer.listCommonPrefixesS3Objects(bucketName, prefix); } }