Skip to content

Commit

Permalink
Merge branch 'master' into issue-1728
Browse files Browse the repository at this point in the history
  • Loading branch information
tardieu authored Mar 3, 2017
2 parents 44f9d70 + 947afe7 commit 5812273
Show file tree
Hide file tree
Showing 48 changed files with 485 additions and 655 deletions.
15 changes: 15 additions & 0 deletions ansible/roles/nginx/templates/nginx.conf.j2
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ http {
server {
listen 443 default ssl;

# match namespace, note while OpenWhisk allows a space and period, they are not permitted here
server_name ~^(?<namespace>[\w@-]+)\.*.*$;

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_certificate /etc/nginx/openwhisk-cert.pem;
Expand All @@ -29,6 +32,18 @@ http {
ssl_prefer_server_ciphers on;
proxy_ssl_session_reuse off;

# proxy to the web action path
location / {
rewrite /(.*) /api/v1/experimental/web/${namespace}/$1 break;
proxy_pass http://{{ groups['controllers']|first }}:{{ controller.port }};
}

# proxy to 'public/html' web action by convention
location = / {
rewrite ^ /api/v1/experimental/web/${namespace}/public/index.html break;
proxy_pass http://{{ groups['controllers']|first }}:{{ controller.port }};
}

location /docs {
proxy_pass http://{{ groups['controllers']|first }}:{{ controller.port }};
}
Expand Down
6 changes: 0 additions & 6 deletions ansible/templates/whisk.properties.j2
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ limits.triggers.fires.perMinute={{ limits.triggers.fires.perMinute }}
consulserver.host={{ groups["consul_servers"]|first }}
controller.host={{ groups["controllers"]|first }}
edge.host={{ groups["edge"]|first }}
{% if groups['entitlement_servers'] is defined %}
entitlement.host={{ groups["entitlement_servers"] | first }}
{% endif %}
kafka.host={{ groups["kafka"]|first }}
loadbalancer.host={{ groups["controllers"]|first }}
router.host={{ groups["edge"]|first }}
Expand All @@ -42,9 +39,6 @@ invoker.hosts={{ groups["invokers"] | join(",") }}

edge.host.uiport=443
edge.host.apiport=443
{% if groups['entitlement_servers'] is defined %}
entitlement.host.port={{ entitlement.port | default }}
{% endif %}
zookeeper.host.port={{ zookeeper.port }}
kafka.host.port={{ kafka.port }}
kafkaras.host.port={{ kafka.ras.port }}
Expand Down
16 changes: 0 additions & 16 deletions common/scala/src/main/scala/whisk/core/WhiskConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ class WhiskConfig(
val dbActivations = this(WhiskConfig.dbActivations)
val dbPrefix = this(WhiskConfig.dbPrefix)

val entitlementHost = this(WhiskConfig.entitlementHostName) + ":" + this(WhiskConfig.entitlementHostPort)
val iamProviderHost = this(WhiskConfig.iamProviderHostName) + ":" + this(WhiskConfig.iamProviderHostPort)

val edgeDockerEndpoint = this(WhiskConfig.edgeDockerEndpoint)
val kafkaDockerEndpoint = this(WhiskConfig.kafkaDockerEndpoint)
val mainDockerEndpoint = this(WhiskConfig.mainDockerEndpoint)
Expand Down Expand Up @@ -259,26 +256,13 @@ object WhiskConfig {
val consulPort = "consul.host.port4"
val invokerHostsList = "invoker.hosts"

private val entitlementHostName = "entitlement.host"
private val entitlementHostPort = "entitlement.host.port"

// using same values as entitlement service
private val iamProviderHostName = "entitlement.host"
private val iamProviderHostPort = "entitlement.host.port"

val edgeHost = Map(edgeHostName -> null, edgeHostApiPort -> null)
val consulServer = Map(consulServerHost -> null, consulPort -> null)
val invokerHosts = Map(invokerHostsList -> null)
val kafkaHost = Map(kafkaHostName -> null, kafkaHostPort -> null)
val controllerHost = Map(controllerHostName -> null, controllerHostPort -> null)
val loadbalancerHost = Map(loadbalancerHostName -> null, loadbalancerHostPort -> null)

// use empty string as default for entitlement/iam host as this is an optional service
// and the way to prevent the configuration checker from failing is to provide a value;
// an empty string is permitted but null is not
val entitlementHost = Map(entitlementHostName -> "", entitlementHostPort -> "")
val iamProviderHost = Map(iamProviderHostName -> "", iamProviderHostPort -> "")

val actionInvokePerMinuteDefaultLimit = "defaultLimits.actions.invokes.perMinute"
val actionInvokeConcurrentDefaultLimit = "defaultLimits.actions.invokes.concurrent"
val actionInvokeSystemOverloadDefaultLimit = "defaultLimits.actions.invokes.concurrentInSystem"
Expand Down
70 changes: 39 additions & 31 deletions core/actionProxy/actionproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class ActionRunner:

# initializes the runner
# @param source the path where the source code will be located (if any)
# @param binary the path where the binary wil be located (may be the same as source code path)
# @param binary the path where the binary will be located (may be the same as source code path)
def __init__(self, source = None, binary = None):
defaultBinary = '/action/exec'
self.source = source if source else defaultBinary
Expand All @@ -49,43 +49,32 @@ def prep():
if 'code' in message:
binary = message['binary'] if 'binary' in message else False
if not binary:
with codecs.open(self.source, 'w', 'utf-8') as fp:
fp.write(message['code'])
# write source epilogue if any
# the message is passed along as it may contain other
# fields relevant to a specific container.
self.epilogue(fp, message)
return True
return self.initCodeFromString(message)
else:
try:
bytes = base64.b64decode(message['code'])
bytes = io.BytesIO(bytes)
archive = zipfile.ZipFile(bytes)
archive.extractall(os.path.dirname(self.binary))
archive.close()
return True
except Exception as e:
print('err',str(e))
return False
return self.initCodeFromZip(message)
else:
return False

if prep():
try:
# write source epilogue if any
# the message is passed along as it may contain other
# fields relevant to a specific container.
self.epilogue(message)

# build the source
self.build()
self.build(message)
except Exception:
None # do nothing, verify will signal failure if binary not executable
# verify the binary exists and is executable
return self.verify()

# optionally appends source to the loaded code during <init>
# @param fp the file stream writer
def epilogue(self, fp, init_arguments):
def epilogue(self, init_arguments):
return

# optionally builds the source code loaded during <init> into an executable
def build(self):
def build(self, init_arguments):
return

# @return True iff binary exists and is executable, False otherwise
Expand Down Expand Up @@ -146,6 +135,25 @@ def error(msg):
except Exception:
return error(last_line)

# initialize code from inlined string
def initCodeFromString(self, message):
with codecs.open(self.source, 'w', 'utf-8') as fp:
fp.write(message['code'])
return True

# initialize code from base64 encoded archive
def initCodeFromZip(self, message):
try:
bytes = base64.b64decode(message['code'])
bytes = io.BytesIO(bytes)
archive = zipfile.ZipFile(bytes)
archive.extractall(os.path.dirname(self.source))
archive.close()
return True
except Exception as e:
print('err',str(e))
return False

proxy = flask.Flask(__name__)
proxy.debug = False
runner = None
Expand Down Expand Up @@ -175,18 +183,10 @@ def init():
else:
response = flask.jsonify({'error': 'The action failed to generate or locate a binary. See logs for details.' })
response.status_code = 502
return response
return complete(response)

@proxy.route('/run', methods=['POST'])
def run():
def complete(response):
# Add sentinel to stdout/stderr
sys.stdout.write('%s\n' % ActionRunner.LOG_SENTINEL)
sys.stdout.flush()
sys.stderr.write('%s\n' % ActionRunner.LOG_SENTINEL)
sys.stderr.flush()
return response

def error():
response = flask.jsonify({'error': 'The action did not receive a dictionary as an argument.' })
response.status_code = 404
Expand All @@ -213,6 +213,14 @@ def error():
response.status_code = 502
return complete(response)

def complete(response):
# Add sentinel to stdout/stderr
sys.stdout.write('%s\n' % ActionRunner.LOG_SENTINEL)
sys.stdout.flush()
sys.stderr.write('%s\n' % ActionRunner.LOG_SENTINEL)
sys.stderr.flush()
return response

def main():
port = int(os.getenv('FLASK_PROXY_PORT', 8080))
server = WSGIServer(('', port), proxy, log=None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,7 @@ import whisk.http.Messages._
* in order to implement the actions API.
*/
object WhiskActionsApi {
def requiredProperties = WhiskServices.requiredProperties ++
WhiskEntityStore.requiredProperties ++
WhiskActivationStore.requiredProperties ++
Map(WhiskConfig.actionSequenceDefaultLimit -> null)
def requiredProperties = Map(WhiskConfig.actionSequenceDefaultLimit -> null)

/** Grace period after action timeout limit to poll for result. */
protected[core] val blockingInvokeGrace = 5 seconds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ import whisk.core.entity._
import whisk.core.entity.types.ActivationStore
import whisk.http.Messages

object WhiskActivationsApi {
def requiredProperties = WhiskActivationStore.requiredProperties
}

/** A trait implementing the activations API. */
trait WhiskActivationsApi
extends Directives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,10 @@

package whisk.core.controller

import scala.concurrent.duration.DurationInt
import scala.concurrent.duration.FiniteDuration
import scala.language.postfixOps

import akka.actor.ActorSystem
import akka.event.Logging.InfoLevel
import whisk.common.Logging
import whisk.core.WhiskConfig
import whisk.core.entitlement._
import whisk.core.entity.ActivationId.ActivationIdGenerator
import whisk.core.iam.NamespaceProvider
import whisk.core.loadBalancer.{ LoadBalancer, LoadBalancerService }

object WhiskServices {

def requiredProperties = WhiskConfig.loadbalancerHost ++ WhiskConfig.consulServer ++ EntitlementProvider.requiredProperties

def consulServer(config: WhiskConfig) = config.consulServer

/**
* Creates instance of an entitlement service.
*/
def entitlementService(config: WhiskConfig, loadBalancer: LoadBalancer, iam: NamespaceProvider, timeout: FiniteDuration = 5 seconds)(
implicit as: ActorSystem, logging: Logging) = {
// remote entitlement service requires a host:port definition. If not given,
// i.e., the value equals ":" or ":xxxx", use a local entitlement flow.
if (config.entitlementHost.startsWith(":")) {
new LocalEntitlementProvider(config, loadBalancer, iam)
} else {
new RemoteEntitlementService(config, loadBalancer, iam, timeout)
}
}

/**
* Creates instance of an identity provider.
*/
def iamProvider(config: WhiskConfig, timeout: FiniteDuration = 5 seconds)(implicit as: ActorSystem, logging: Logging) = {
new NamespaceProvider(config, timeout)
}

/**
* Creates an instance of a Load Balancer component.
*
* @param config the configuration with loadbalancerHost defined
* @return a load balancer component
*/
def makeLoadBalancerComponent(config: WhiskConfig)(implicit as: ActorSystem, logging: Logging) = new LoadBalancerService(config, InfoLevel)

}
import whisk.core.loadBalancer.LoadBalancer

/**
* A trait which defines a few services which a whisk microservice may rely on.
Expand All @@ -76,9 +31,6 @@ trait WhiskServices {
/** An entitlement service to check access rights. */
protected val entitlementProvider: EntitlementProvider

/** An identity provider. */
protected val iam: NamespaceProvider

/** A generator for new activation ids. */
protected val activationIdFactory: ActivationIdGenerator

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@

package whisk.core.controller

import scala.concurrent.Future
import scala.language.postfixOps
import scala.util.Failure
import scala.util.Success
import scala.util.Try

import shapeless.HNil
import spray.http.StatusCodes.InternalServerError
import spray.http.StatusCodes.RequestEntityTooLarge
import spray.httpx.SprayJsonSupport._
import spray.routing.Directive0
Expand Down Expand Up @@ -120,31 +116,10 @@ trait WhiskCollectionAPI
// produce all entities in the requested namespace UNLESS the subject is
// entitled to them which for now means they own the namespace. If the
// subject does not own the namespace, then exclude packages that are private
val checkIfSubjectOwnsResource = if (listRequiresPrivateEntityFilter) {
if (resource.namespace.root.asString == user.subject.asString) {
// bypass iam if namespace is owned by subject
// don't need to exclude private packages owned by subject
Future.successful(false)
} else {
iam.namespaces(user.subject) map {
// don't need to exclude private packages in any namespace owned by subject
_.contains(resource.namespace.root.asString) == false
}
}
} else Future.successful(false)

onComplete(checkIfSubjectOwnsResource) {
case Success(excludePrivate) =>
logging.info(this, s"[LIST] exclude private entities: required == $excludePrivate")
list(user, resource.namespace, excludePrivate)
case Failure(r: RejectRequest) =>
logging.info(this, s"[LIST] namespaces lookup failed: ${r.message}")
terminate(r.code, r.message)
case Failure(t) =>
logging.error(this, s"[LIST] namespaces lookup failed: ${t.getMessage}")
terminate(InternalServerError)
val excludePrivate = listRequiresPrivateEntityFilter && resource.namespace.root != user.namespace
logging.info(this, s"[LIST] exclude private entities: required == $excludePrivate")
list(user, resource.namespace, excludePrivate)

}
case _ => reject
}
}
Expand Down
Loading

0 comments on commit 5812273

Please sign in to comment.