All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.symphony.oss.fugue.aws.sns.SnsPublisherAdmin Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
/*
 *
 *
 * Copyright 2018 Symphony Communication Services, LLC.
 *
 * Licensed to The Symphony Software Foundation (SSF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.symphony.oss.fugue.aws.sns;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.sns.model.AmazonSNSException;
import com.amazonaws.services.sns.model.CreateTopicRequest;
import com.amazonaws.services.sns.model.CreateTopicResult;
import com.amazonaws.services.sns.model.GetTopicAttributesResult;
import com.amazonaws.services.sns.model.ListSubscriptionsByTopicResult;
import com.amazonaws.services.sns.model.NotFoundException;
import com.amazonaws.services.sns.model.SetTopicAttributesRequest;
import com.amazonaws.services.sns.model.Subscription;
import com.amazonaws.services.sns.model.Tag;
import com.amazonaws.services.sns.model.TagResourceRequest;
import com.symphony.oss.fugue.Fugue;
import com.symphony.oss.fugue.naming.TopicName;
import com.symphony.oss.fugue.pubsub.IPublisher;
import com.symphony.oss.fugue.pubsub.IPublisherAdmin;

/**
 * The admin variation of an SnsPublisherManager.
 * 
 * @author Bruce Skingle
 *
 */
public class SnsPublisherAdmin extends SnsPublisherBase implements IPublisherAdmin
{
  private static final Logger log_            = LoggerFactory.getLogger(SnsPublisherAdmin.class);

  private static final Object POLICY = "Policy";

  private Set      topics_ = new HashSet<>();
  private List           tags_   = new LinkedList<>();
  
//  /**
//   * Constructor.
//   * 
//   * @param config      The configuration provider.
//   * @param nameFactory A name factory.
//   * @param region      The AWS region to use.
//   * @param accountId   The AWS numeric account ID 
//   */
//  public SnsPublisherAdmin(IConfiguration config, INameFactory nameFactory, String region, String accountId)
//  {
//    super(config, nameFactory, region, accountId, true);
//  }
  
  private SnsPublisherAdmin(Builder builder)
  {
    super(SnsPublisherAdmin.class, builder);
    
    for(Entry tagEntry : builder.tags_.entrySet())
    {
      tags_.add(new Tag()
          .withKey(tagEntry.getKey())
          .withValue(tagEntry.getValue())
          );
    }
  }

  /**
   * Concrete builder.
   * 
   * @author Bruce Skingle
   */
  public static class Builder extends SnsPublisherBase.Builder
  {
    private  Map    tags_ = new HashMap<>();
    
    /**
     * Constructor.
     */
    public Builder()
    {
      super(Builder.class);
    }
    
    /**
     * Add the given tags to created queues.
     * Multiple calls to this method are cumulative.
     * 
     * @param tags Tags to add.
     * 
     * @return this (fluent method)
     */
    public Builder withTags(Map tags)
    {
      tags_.putAll(tags);
      
      return self();
    }

    @Override
    protected SnsPublisherAdmin construct()
    {
      return new SnsPublisherAdmin(this);
    }
  }

  @Override
  public boolean validateTopic(TopicName topicName)
  {
    // topics may not have been created yet
    return true;
  }

  @Override
  public IPublisher getPublisherByName(String topicId)
  {
    topics_.add(nameFactory_.getTopicName(topicId));
    
    return super.getPublisherByName(topicId);
  }

  @Override
  public IPublisher getPublisherByName(String serviceId, String topicId)
  {
    topics_.add(nameFactory_.getTopicName(serviceId, topicId));
    
    return super.getPublisherByName(serviceId, topicId);
  }
  
  @Override
  public IPublisher getPublisherByName(TopicName topicName)
  {
    topics_.add(topicName);
    
    return super.getPublisherByName(topicName);
  }
  
  private String getTopicPolicy(String topicArn)
  {
    StringBuilder s = new StringBuilder("{" + 
          "\"Version\":\"2008-10-17\"," + 
          "\"Id\":\"__default_policy_ID\"," + 
          "\"Statement\":[" + 
            "{" + 
              "\"Sid\":\"__default_statement_ID\"," + 
              "\"Effect\":\"Allow\"," + 
              "\"Principal\":{" + 
                "\"AWS\":\"*\"" + 
              "}," + 
              "\"Action\":[" + 
                "\"SNS:GetTopicAttributes\"," + 
                "\"SNS:SetTopicAttributes\"," + 
                "\"SNS:AddPermission\"," + 
                "\"SNS:RemovePermission\"," + 
                "\"SNS:DeleteTopic\"," +
                "\"SNS:Subscribe\"," + 
                "\"SNS:ListSubscriptionsByTopic\"," + 
                "\"SNS:Publish\"," +  
                "\"SNS:Receive\"" + 
              "]," + 
              "\"Resource\":\"" + topicArn + "\"," + 
              "\"Condition\":{" + 
                "\"StringEquals\":{" + 
                  "\"AWS:SourceOwner\":\"" + accountId_ + "\"" + 
                "}" + 
              "}" + 
            "}");
    
    if(subscriberAccountIds_.size() > 0)
    {
      s.append( 
            ",{" + 
            "\"Sid\":\"__console_sub_0\"," + 
            "\"Effect\":\"Allow\"," + 
            "\"Principal\":{" + 
              "\"AWS\":[");
    
      boolean first = true;
      
      for(String subscriberAccountId : subscriberAccountIds_)
      {
        if(first)
          first = false;
        else
          s.append(",");
        
        s.append("\"arn:aws:iam::");
        s.append(subscriberAccountId);
        s.append(":root\"");
      }
      
      s.append( 
          "]" + 
              "}," +
              "\"Action\":[" + 
                "\"SNS:Subscribe\"," + 
                "\"SNS:Receive\"" + 
              "]," + 
              "\"Resource\":\"" + topicArn + "\"" + 
            "}");
    }
    
    s.append( 
          "]" + 
        "}");
    
    return s.toString();
  }

  @Override
  public void createTopics(boolean dryRun)
  {
    for(TopicName topicName : topics_)
    {
      if(topicName.isLocal())
      {
        String topicArn = getTopicARN(topicName);
        
        try
        {
          GetTopicAttributesResult topicAttributes = snsClient_.getTopicAttributes(topicArn);
          
          log_.info("Topic " + topicName + " exists as " + topicArn + " with attributes " + topicAttributes.getAttributes());

          String topicPolicy = getTopicPolicy(topicArn);
          
          if(topicPolicy.equals(topicAttributes.getAttributes().get(POLICY)))
          {
            log_.info("Topic policy is OK");
          }
          else
          {
            updateTopicPolicy(topicArn, topicPolicy);
          }
        }
        catch(NotFoundException e)
        {
          if(dryRun)
          {
            log_.info("Topic " + topicName + " does not exist and would be created (dry run).");
          }
          else
          {
            CreateTopicRequest createTopicRequest = new CreateTopicRequest(topicName.toString());
            CreateTopicResult createTopicResult = snsClient_.createTopic(createTopicRequest);

            log_.info("Created topic " + topicName + " as " + createTopicResult.getTopicArn());
            
            updateTopicPolicy(topicArn, getTopicPolicy(topicArn));
          }
        }
        
        snsClient_.tagResource(new TagResourceRequest()
            .withResourceArn(topicArn)
            .withTags(tags_)
            .withTags(new Tag().withKey(Fugue.TAG_FUGUE_ITEM).withValue(topicName.toString()))
            );
      }
      else
      {
        log_.info("Topic " + topicName + " does not belong to this service and is unaffected.");
      }
    }
  }

  private void updateTopicPolicy(String topicArn, String topicPolicy)
  {
    log_.info("Updating topic policy...");
    
    snsClient_.setTopicAttributes(new SetTopicAttributesRequest()
        .withTopicArn(topicArn)
        .withAttributeName("Policy")
        .withAttributeValue(topicPolicy)
        );
    
    log_.info("Updating topic policy...Done.");
  }

//  @Override
//  public void createTopics(boolean dryRun)
//  {
//    for(SnsPublisher publisher : publishers_)
//    {
//      CreateTopicRequest createTopicRequest = new CreateTopicRequest(topicName.toString());
//      CreateTopicResult createTopicResult = snsClient_.createTopic(createTopicRequest);
//      //print TopicArn
//      log_.info("Created topic " + topicName + " as " + createTopicResult.getTopicArn());
//      //get request id for CreateTopicRequest from SNS metadata   
//      System.out.println("CreateTopicRequest - " + snsClient_.getCachedResponseMetadata(createTopicRequest));
//    }
//  }

  @Override
  public void deleteTopics(boolean dryRun)
  {
    deleteTopics(dryRun, topics_);
  }

  private void deleteTopics(boolean dryRun, Set topics)
  {
    for(TopicName topicName : topics)
    {
      if(topicName.isLocal())
      {
        String topicArn = getTopicARN(topicName);
        
        try
        {
          ListSubscriptionsByTopicResult topicSubscriptions = snsClient_.listSubscriptionsByTopic(topicArn);
          List subscriptions = topicSubscriptions.getSubscriptions();
          
          if(dryRun)
          {
            if(subscriptions.isEmpty())
            {
              log_.info("Topic " + topicName + " has no subscriptions and would be deleted (dry run).");
            }
            else
            {
              log_.warn("Topic " + topicName + " has " + subscriptions.size() + " subscriptions and cannot be deleted (dry run).");
              
              for(Subscription s : subscriptions)
              {
                log_.info("Topic " + topicName + " has subscription to " + s.getEndpoint() + " as " + s.getSubscriptionArn());
              }
            }
          }
          else
          {
            if(subscriptions.isEmpty())
            {
              log_.info("Deleting topic " + topicName + "...");
              
              try
              {
                snsClient_.deleteTopic(topicArn);
                log_.info("Deleted topic " + topicName);
              }
              catch(AmazonSNSException e)
              {
                log_.error("Failed to delete topic " + topicName, e);
              }
            }
            else
            {
              log_.warn("Topic " + topicName + " has " + subscriptions.size() + " subscriptions and cannot be deleted (dry run).");
              
              for(Subscription s : subscriptions)
              {
                log_.info("Topic " + topicName + " has subscription to " + s.getEndpoint() + " as " + s.getSubscriptionArn());
              }
            }
          }
        }
        catch(NotFoundException e)
        {
          log_.info("Topic " + topicName + " does not exist.");
        }
      }
      else
      {
        log_.info("Topic " + topicName + " does not belong to this service and is unaffected.");
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy