/**
* Copyright 2017 Netflix, Inc.
*
* 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.netflix.raigad.aws;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.netflix.raigad.configuration.IConfiguration;
import com.netflix.raigad.identity.IMembership;
import com.netflix.raigad.identity.IRaigadInstanceFactory;
import com.netflix.raigad.identity.InstanceManager;
import com.netflix.raigad.identity.RaigadInstance;
import com.netflix.raigad.scheduler.SimpleTimer;
import com.netflix.raigad.scheduler.Task;
import com.netflix.raigad.scheduler.TaskTimer;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
/**
* This class will associate public IP's with a new instance so they can talk across the regions.
*
* Requirements:
* 1. Nodes in the same region needs to be able to talk to each other.
* 2. Nodes in other regions needs to be able to talk to the others in the other region.
*
* Assumptions:
* 1. IRaigadInstanceFactory will provide the membership and will be visible across the regions
* 2. IMembership amazon or any other implementation which can tell if the instance is a
* part of the group (ASG in Amazon's case).
*/
@Singleton
public class UpdateSecuritySettings extends Task {
private static final Logger logger = LoggerFactory.getLogger(UpdateSecuritySettings.class);
public static final String JOB_NAME = "Update_SG";
public static boolean firstTimeUpdated = false;
private static final Random RANDOM = new Random();
private final IMembership membership;
private final IRaigadInstanceFactory factory;
@Inject
public UpdateSecuritySettings(IConfiguration config, IMembership membership, IRaigadInstanceFactory factory) {
super(config);
this.membership = membership;
this.factory = factory;
}
/**
* Master nodes execute this at the specified interval, others run only on startup
*/
@Override
public void execute() {
int port = config.getTransportTcpPort();
List<String> acls = membership.listACL(port, port);
// Get instances based on node types (tribe / non-tribe)
List<RaigadInstance> instances = getInstanceList();
// Iterate cluster nodes and build a list of IP's
List<String> ipsToAdd = Lists.newArrayList();
List<String> currentRanges = Lists.newArrayList();
for (RaigadInstance instance : instances) {
String range = instance.getHostIP() + "/32";
currentRanges.add(range);
if (!acls.contains(range)) {
ipsToAdd.add(range);
}
}
if (ipsToAdd.size() > 0) {
membership.addACL(ipsToAdd, port, port);
firstTimeUpdated = true;
}
// Create a list of IP's to remove
List<String> ipsToRemove = Lists.newArrayList();
for (String acl : acls) {
// Remove if not found
if (!currentRanges.contains(acl)) {
ipsToRemove.add(acl);
}
}
if (ipsToRemove.size() > 0) {
membership.removeACL(ipsToRemove, port, port);
firstTimeUpdated = true;
}
}
private List<RaigadInstance> getInstanceList() {
List<RaigadInstance> instances = new ArrayList<RaigadInstance>();
if (config.amISourceClusterForTribeNode()) {
List<String> tribeClusters = new ArrayList<String>(Arrays.asList(StringUtils.split(config.getCommaSeparatedTribeClusterNames(), ",")));
assert (tribeClusters.size() != 0) : "I am a source cluster but I need one or more tribe clusters";
for (String tribeClusterName : tribeClusters) {
instances.addAll(factory.getAllIds(tribeClusterName));
}
}
// Adding the current cluster
instances.addAll(factory.getAllIds(config.getAppName()));
if (config.isDebugEnabled()) {
for (RaigadInstance instance : instances) {
logger.debug(instance.toString());
}
}
return instances;
}
public static TaskTimer getTimer(InstanceManager instanceManager) {
// Only master nodes will update security group settings
if (!instanceManager.isMaster()) {
return new SimpleTimer(JOB_NAME);
}
else {
return new SimpleTimer(JOB_NAME, 120 * 1000 + RANDOM.nextInt(120 * 1000));
}
}
@Override
public String getName() {
return JOB_NAME;
}
}