AppController/djinn.rb
# A SOAP-exposed method that callers can use to tell this AppController that
# an app hosted in this cloud needs to have its nginx reverse proxy serving
# HTTP and HTTPS traffic on different ports.
#
# Args:
#   appid: A String that names the application already running in this
#     deployment that should be relocated.
#   http_port: A String or Fixnum that names the port that should be used to
#     serve HTTP traffic for this app.
#   https_port: A String or Fixnum that names the port that should be used to
#     serve HTTPS traffic for this app.
#   secret: A String that authenticates callers.
# Returns:
#   "OK" if the relocation occurred successfully, and a String containing the
#   reason why the relocation failed in all other cases.
A SOAP-exposed method that callers can use to tell this AppController that
an app hosted in this cloud needs to have its nginx reverse proxy serving
HTTP and HTTPS traffic on different ports.

A String that names the application already running in this
deployment that should be relocated.
http_port: A String or Fixnum that names the port that should be used to
serve HTTP traffic for this app.
https_port: A String or Fixnum that names the port that should be used to
serve HTTPS traffic for this app.
secret: A String that authenticates callers.
Returns:
"OK" if the relocation occurred successfully, and a String containing the
reason why the relocation failed in all other cases.
A SOAP - exposed method that callers can use to tell this AppController that an app hosted in this cloud needs to have its nginx reverse proxy serving HTTP and HTTPS traffic on different ports . A String that names the application already running in this deployment that should be relocated . http_port : A String or Fixnum that names the port that should be used to serve HTTP traffic for this app . https_port : A String or Fixnum that names the port that should be used to serve HTTPS traffic for this app . secret : A String that authenticates callers . Returns : " OK " if the relocation occurred successfully and a String containing the reason why the relocation failed in all other cases .
def relocate_app(appid, http_port, https_port, secret)
    return BAD_SECRET_MSG unless valid_secret?(secret)
    Djinn.log_debug("Received relocate_app for #{appid} for " +
        "http port #{http_port} and https port #{https_port}.")
    unless my_node.is_shadow?
      Djinn.log_debug("Sending relocate_app for #{appid} to #{get_shadow}.")
      acc = AppControllerClient.new(get_shadow.private_ip, @@secret)
      begin
        return acc.relocate_app(appid, http_port, https_port)
      rescue FailedNodeException
        Djinn.log_warn("Failed to forward relocate_app call to #{get_shadow}.")
        return NOT_READY
      end
    end
    return "Error: Can't relocate the #{appid} app." if RESERVED_APPS.include?(appid)
    begin
      http_port = Integer(http_port)
      https_port = Integer(https_port)
    rescue ArgumentError
      Djinn.log_warn("relocate_app received invalid port values.")
      return INVALID_REQUEST
    end
    APPS_LOCK.synchronize {
      if @app_info_map[appid].nil? or @app_info_map[appid]['nginx'].nil? or
          @app_info_map[appid]['nginx_https'].nil? or
          @app_info_map[appid]['haproxy'].nil?
        Djinn.log_warn("Unable to relocate due to missing app settings for: #{appid}.")
        return INVALID_REQUEST
      end
      if http_port != 80 and
         (http_port < Nginx::START_PORT or http_port > Nginx::END_PORT)
        return "Error: HTTP port must be 80, or in the range" +
          " #{Nginx::START_PORT}-#{Nginx::END_PORT}."
      end
      if (https_port < Nginx::START_PORT - Nginx::SSL_PORT_OFFSET or https_port >
          Nginx::END_PORT - Nginx::SSL_PORT_OFFSET) and https_port != 443
        return "Error: HTTPS port must be 443, or in the range " +
           "#{Nginx::START_PORT - Nginx::SSL_PORT_OFFSET}-" +
           "#{Nginx::END_PORT - Nginx::SSL_PORT_OFFSET}."
      end
      if find_lowest_free_port(http_port, http_port, appid) < 0
        return "Error: requested http port is already in use."
      end
      if find_lowest_free_port(https_port, https_port, appid) < 0
        return "Error: requested https port is already in use."
      end
      @app_info_map[appid]['nginx'] = http_port
      @app_info_map[appid]['nginx_https'] = https_port
    }
    Djinn.log_info("Assigned ports for relocated app #{appid}.")
    my_public = my_node.public_ip
    port_file = "#{APPSCALE_CONFIG_DIR}/port-#{appid}.txt"
    HelperFunctions.write_file(port_file, http_port)
    Thread.new {
      uac = UserAppClient.new(my_node.private_ip, @@secret)
      success = uac.add_instance(appid, my_public, http_port, https_port)
      unless success
        Djinn.log_warn("Failed to store relocation ports for #{appid} via the uaserver.")
        return
      end
      @nodes.each { |node|
        if node.private_ip != my_node.private_ip
          HelperFunctions.scp_file(port_file, port_file, node.private_ip,
            node.ssh_key)
        end
        next unless node.is_appengine?
        app_manager = AppManagerClient.new(node.private_ip)
        begin
          app_manager.restart_app_instances_for_app(appid,
            @app_info_map[appid]['language'])
        rescue FailedNodeException
          Djinn.log_warn("#{appid} may have not restarted on #{node.private_ip} upon relocate.")
        end
      }
    }
    MonitInterface.restart("xmpp-#{appid}")
    return "OK"
  end
def relocate_app ( appid , http_port , https_port , secret ) return BAD_SECRET_MSG unless valid_secret? ( secret ) Djinn . log_debug ( "Received relocate_app for #{appid} for " + "http port #{http_port} and https port #{https_port}." ) unless my_node . is_shadow? Djinn . log_debug ( "Sending relocate_app for #{appid} to #{get_shadow}." ) acc = AppControllerClient . new ( get_shadow . private_ip , @@secret ) begin return acc . relocate_app ( appid , http_port , https_port ) rescue FailedNodeException Djinn . log_warn ( "Failed to forward relocate_app call to #{get_shadow}." ) return NOT_READY end end return "Error: Can't relocate the #{appid} app." if RESERVED_APPS . include? ( appid ) begin http_port = Integer ( http_port ) https_port = Integer ( https_port ) rescue ArgumentError Djinn . log_warn ( "relocate_app received invalid port values." ) return INVALID_REQUEST end APPS_LOCK . synchronize { if @app_info_map [ appid ] . nil? or @app_info_map [ appid ] [ 'nginx' ] . nil? or @app_info_map [ appid ] [ 'nginx_https' ] . nil? or @app_info_map [ appid ] [ 'haproxy' ] . nil? Djinn . log_warn ( "Unable to relocate due to missing app settings for: #{appid}." ) return INVALID_REQUEST end if http_port != 80 and ( http_port < Nginx :: START_PORT or http_port > Nginx :: END_PORT ) return "Error: HTTP port must be 80, or in the range" + " #{Nginx::START_PORT}-#{Nginx::END_PORT}." end if ( https_port < Nginx :: START_PORT - Nginx :: SSL_PORT_OFFSET or https_port > Nginx :: END_PORT - Nginx :: SSL_PORT_OFFSET ) and https_port != 443 return "Error: HTTPS port must be 443, or in the range " + "#{Nginx::START_PORT - Nginx::SSL_PORT_OFFSET}-" + "#{Nginx::END_PORT - Nginx::SSL_PORT_OFFSET}." end if find_lowest_free_port ( http_port , http_port , appid ) < 0 return "Error: requested http port is already in use." end if find_lowest_free_port ( https_port , https_port , appid ) < 0 return "Error: requested https port is already in use." end @app_info_map [ appid ] [ 'nginx' ] = http_port @app_info_map [ appid ] [ 'nginx_https' ] = https_port } Djinn . log_info ( "Assigned ports for relocated app #{appid}." ) my_public = my_node . public_ip port_file = "#{APPSCALE_CONFIG_DIR}/port-#{appid}.txt" HelperFunctions . write_file ( port_file , http_port ) Thread . new { uac = UserAppClient . new ( my_node . private_ip , @@secret ) success = uac . add_instance ( appid , my_public , http_port , https_port ) unless success Djinn . log_warn ( "Failed to store relocation ports for #{appid} via the uaserver." ) return end @nodes . each { | node | if node . private_ip != my_node . private_ip HelperFunctions . scp_file ( port_file , port_file , node . private_ip , node . ssh_key ) end next unless node . is_appengine? app_manager = AppManagerClient . new ( node . private_ip ) begin app_manager . restart_app_instances_for_app ( appid , @app_info_map [ appid ] [ 'language' ] ) rescue FailedNodeException Djinn . log_warn ( "#{appid} may have not restarted on #{node.private_ip} upon relocate." ) end } } MonitInterface . restart ( "xmpp-#{appid}" ) return "OK" end
A SOAP-exposed method that callers can use to tell this AppController that
an app hosted in this cloud needs to have its nginx reverse proxy serving
HTTP and HTTPS traffic on different ports.
A SOAP - exposed method that callers can use to tell this AppController that an app hosted in this cloud needs to have its nginx reverse proxy serving HTTP and HTTPS traffic on different ports .
# We need to send the call to the shadow. # Sanity checks on app ID and settings. # First, only let users relocate apps to ports that the firewall has open # for App Engine apps. # We need to check if http_port and https_port are already in use by # another application, so we do that with find_lowest_free_port and we # fix the range to the single port. # Next, rewrite the nginx config file with the new ports # Finally, the AppServer takes in the port to send Task Queue tasks to # from a file. Update the file and restart the AppServers so they see # the new port. Do this in a separate thread to avoid blocking the caller. # Notify the UAServer about the new ports. # Once we've relocated the app, we need to tell the XMPPReceiver about the # app's new location.
appid
<not_specific>
http_port
<not_specific>
https_port
<not_specific>
secret
<not_specific>