initial commit
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b9b567a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,178 @@
+# Istio-Apigee-Demo
+This demo demostrate how Apigee works with Istio, how services deployed in Istio framework can make use of Apigee features like API key verification and Analytics.
+
+## Pre-requisites
+
+#### Kubernetes cluster with > 3 nodes
+
+You can use Google Container Engine to launch the Kubenertees Cluster - https://cloud.google.com/container-engine/
+
+#### kubectl utility in your local machine
+Find Instructions here - https://kubernetes.io/docs/tasks/tools/install-kubectl/
+
+#### istioctl utility in your local machine
+- Download the right istio release from here - https://github.com/istio/istio/releases
+- Extract it and copy the istioctl binary from ```bin/istioctl``` to ```/usr/local/bin```
+- Make sure you can run ```istioctl --help```
+
+#### Microgateway enable Apigee org
+Contact Apigee Support
+
+## Setup
+
+#### Point kubectl to your kubernetes cluster
+Find instructions here - https://istio.io/docs/tasks/installing-istio.html#prerequisites
+
+#### Configuring Apigee parameters
+You you need to provide parameters like like - **org_name**, **env**, **microgateway_key**, **microgateway_secret** for the mixer adapter to work with your org.
+
+- Install edgemicrogateway in your local machine
+Find instructions here - http://docs.apigee.com/microgateway/latest/installing-edge-microgateway
+- Generate key and secret so that edge can authenticate the apigee adapter.
+Find instructions here - http://docs.apigee.com/microgateway/latest/setting-and-configuring-edge-microgateway#Part1
+
+Go ahead and replace the respective values in the following file
+```
+config/testdata/configroot/scopes/global/adapters.yml
+```
+
+Create kube secret for this file, which later get mount to istio mixer
+```
+kubectl create secret generic apigee-mixer-adapter --from-file=./config/testdata/configroot/scopes/global/adapters.yml
+```
+
+#### Install Istio
+
+1. Run the following command to determine if your cluster has RBAC (Role-Based Access Control) enabled:
+```
+kubectl api-versions | grep rbac
+```
+2. If the command displays an error, or does not display anything, it means the cluster does not support RBAC, and you can skip the next 2 steps
+
+3. If the command displays ‘beta’ version, or both ‘alpha’ and ‘beta’, please apply istio-rbac-beta.yaml configuration:
+```
+kubectl apply -f install/kubernetes/istio-rbac-beta.yaml
+```
+4. If the command displays only ‘alpha’ version, please apply istio-rbac-alpha.yaml configuration:
+```
+kubectl apply -f install/kubernetes/istio-rbac-alpha.yaml
+```
+5. Install Istio’s core components.
+```
+kubectl apply -f setup/istio.yaml
+```
+**NOTE** : In the mixer section of istio.yaml file use can see that we are using custom docker image(mixer:apigeev1) which  contains apigee adapter
+
+This command will install Istio-Manager, Mixer with Apigee adapter, Ingress-Controller, Egress-Controller core components.
+
+
+## Demo
+
+For this demo we will use the simple httpbin application
+
+**Deploy App**
+
+Run the following commands to deploy httpbin application
+```
+source istio.VERSION
+kubectl apply -f <(istioctl kube-inject -f demo/apps/httpbin/httpbin.yml)
+```
+
+**Get IP of Ingress**
+- Get the IP address of ingress to make calls to the deloyed application
+```
+kubectl get ingress -o wide
+```
+- If you don't find public IP from the above command, use the private IP of the ingress service and make sure you make calls from inside the kube network since you are using privateIP. You the following command to get private IP of ingress
+```
+kubectl get svc istio-ingress
+```
+```
+export GATEWAY=<IP_of_Ingress>
+```
+
+**Make calls before creating mixer rules**
+```
+curl $GATE/get
+```
+you will see response code **200**
+```
+200
+```
+
+**Creating mixer rules**
+Let's create mixer rules so that apigee adapter is triggerd on your API calls
+Currently apigee adapter provides **APIkey verfification** and **Analytics** features. 
+
+Run the following command to enable apigee for httpbin application.
+```
+istioctl mixer rule create global httpbin.default.svc.cluster.local -f config/rules.yml
+```
+
+**Make calls after creating mixer rules**
+```
+curl -o /dev/null -s -w "%{http_code}\n" $GATE/get
+```
+Now since APIkey verfication fails, you will see reponse code either **403** or **500**
+```
+403
+```
+Try with a wrong API key
+```
+curl -o /dev/null -s -w "%{http_code}\n" -H 'apikey: dsjcajcasbch' $GATE/get
+```
+```
+403
+```
+BTW **Analytics** is already being recorded for your **httpbin** service, you should be able to see failure API calls in the Apigee UI(might take 10 minutes to showup).
+
+You need to get the right API key from Apigee UI to be able to make successful calls.
+
+If you are familiar with Apigee following steps will make more sense to you
+ - Create a Apigee Product(```https://enterprise.apigee.com/platform/{org-name}/products```). This is how company expose their API to outside world
+ - Creata 2 Developers(```https://enterprise.apigee.com/platform/{org-name}/developers```). Developers can use the exposed APIs by creating Apigee App. 
+ - Create 2 Apps(```https://enterprise.apigee.com/platform/{org-name}/app```). This will provission credentials to access the APIs. While creating the make sure you select the above created product and each developer.
+ 
+**NOTE** : Create 2 developers and 2 apps respectively so that we can see more intresting stuff in analytics.
+
+Now that you have credentails to make API calls, let's make some more calls. This time pass the Consumer Key of the App that yo just created as header parameter.
+
+```
+curl -o /dev/null -s -w "%{http_code}\n" -H 'apikey: <ConsumerKey>' $GATE/get
+```
+You should see response code 200
+```
+200
+```
+
+Yayy !!
+
+Let's dig more on **Analytics**, for the demo purpose make few more calls with API keys of both apps.
+
+**Let's check the API performance of your httpbin API (data might data 10 minutues to show up in UI)**
+
+Goto https://enterprise.apigee.com/platform/<org_name>/proxy-performance
+
+You should be able to something like below
+
+![](images/istio.png)
+
+The Proxy Performance dashboard helps you see API traffic patterns and processing times. You can easily visualize how much traffic your APIs generate and how long it takes for API calls to be processed, from the time they are received by Apigee Edge until they are returned to the client app.
+
+**Let's check developer engagement of your httpbin API (data might data 10 minutues to show up in UI)**
+
+Goto https://enterprise.apigee.com/platform/<org_name>/developer-engagement
+
+You should be able to something like below
+![](http://d3grn7b5c5cnw5.cloudfront.net/sites/docs/files/developer-engagement.png)
+
+The Developer Engagement dashboard tells you which of your registered app developers are generating the most API traffic. For each of your developers, you can find out who is generating the most API traffic and the most errors. For example, if a particular developer's app is generating a lot of errors relative to other developers, you can pro-actively address the problem with that developer.
+
+Read more about developer engagement here - 
+http://docs.apigee.com/analytics-services/content/partner-engagement-dashboard
+
+Read more about Apigee analytics here - 
+https://apigee.com/about/products/apigee-edge-and-apis/edge-analytics-services
+https://apigee.com/about/products/apigee-edge-and-apis/edge-analytics-services
+
+-------------------------------------------------------------
diff --git a/config/rules.yml b/config/rules.yml
new file mode 100644
index 0000000..f53a097
--- /dev/null
+++ b/config/rules.yml
@@ -0,0 +1,71 @@
+subject: namespace:ns
+revision: "2022"
+rules:
+- selector: # must be empty for preprocessing adapters
+  aspects:
+  # Fetch the API key and use it to set various request attributes.
+  # This is where the adapter should cache, because this one gets called three times
+  # (check, quota, and report)
+  - kind: attributes
+    adapter: apigeeAttributes
+    params:
+      input_expressions:
+        apiKey: request.headers["apikey"] | "INVALID_KEY"
+        requestPath: request.path | "/"
+      attribute_bindings:
+        authorization.success.string: successString
+        authorization.success: success
+        authorization.client.id: clientID
+        authorization.application.name: applicationName
+        authorization.apiproduct.name: apiProduct
+  # In "check" reject the request if the key is invalid. It'd be nice if we could
+  # customize the error here. We could build yet another adapter for that purpose!
+  - kind: lists
+    adapter: authorizationChecker
+    params:
+      checkExpression: authorization.success.string
+  # This is a simpler way to achieve the above!
+  #- kind: lists
+  #  adapter: apigeeAPIKey
+  #  params:
+  #    checkExpression: request.headers["apikey"]
+  - kind: quotas
+    params:
+      quotas:
+      - descriptorName: RequestCount
+        maxAmount: 5000
+        expiration: 1s
+  #- kind: access-logs
+  #  params:
+  #    logName: accesslog.default
+  #    log:
+  #      descriptorName: accesslog.common
+  #      labels:
+  #        originIp: source.ip
+  #        sourceUser: source.uid
+  #        timestamp: request.time
+  #        method: request.method | "http"
+  #        url: request.path
+  #        protocol: request.scheme
+  #        responseCode: response.code
+  #        responseSize: response.size
+  - kind: access-logs
+    adapter: apigeeAnalytics
+    params:
+      logName: accesslog.apigee
+      log:
+        descriptorName: accesslog.apigee
+        labels:
+          sourceIP: source.ip
+          urlPath: request.path
+          hostHeader: request.host
+          httpMethod: request.method
+          userAgent: request.useragent
+          requestTime: request.time
+          responseTime: response.time
+          responseCode: response.code | 200
+          proxyName: target.service | "istio_service"
+          proxyRevision: proxy.revision | 1
+          clientID: authorization.client.id | ""
+          applicationName: authorization.application.name | ""
+          apiProduct: authorization.apiproduct.name | ""
diff --git a/config/rules.yml.backup b/config/rules.yml.backup
new file mode 100644
index 0000000..2a00311
--- /dev/null
+++ b/config/rules.yml.backup
@@ -0,0 +1,46 @@
+subject: namespace:ns
+revision: "2022"
+rules:
+- selector: # must be empty for preprocessing adapters
+  aspects:
+  # Fetch the API key and use it to set various request attributes.
+  # This is where the adapter should cache, because this one gets called three times
+  # (check, quota, and report)
+  - kind: attributes
+    adapter: apigeeAttributes
+    params:
+      input_expressions:
+        apiKey: request.headers["apikey"] | "INVALID_KEY"
+        requestPath: request.path | "/"
+      attribute_bindings:
+        authorization.success.string: successString
+        authorization.success: success
+        authorization.client.id: clientID
+        authorization.application.name: applicationName
+        authorization.apiproduct.name: apiProduct
+  # In "check" reject the request if the key is invalid. It'd be nice if we could
+  # customize the error here. We could build yet another adapter for that purpose!
+  - kind: lists
+    adapter: authorizationChecker
+    params:
+      checkExpression: authorization.success.string
+  - kind: attributes
+    adapter: apigeeAnalytics
+    params:
+      logName: accesslog.apigee
+      log: 
+        descriptorName: accesslog.apigee
+        labels:
+          sourceIP: source.ip
+          urlPath: request.path
+          hostHeader: request.host
+          httpMethod: request.method
+          userAgent: request.useragent
+          requestTime: request.time
+          responseTime: response.time
+          responseCode: response.code | 200
+          proxyName: proxy.name | "istio"
+          proxyRevision: proxy.revision | 1
+          clientID: authorization.client.id | ""
+          applicationName: authorization.application.name | ""
+          apiProduct: authorization.apiproduct.name | ""
diff --git a/config/testdata/BUILD b/config/testdata/BUILD
new file mode 100644
index 0000000..0f4ca32
--- /dev/null
+++ b/config/testdata/BUILD
@@ -0,0 +1,16 @@
+load("@bazel_tools//tools/build_defs/pkg:pkg.bzl", "pkg_tar")
+
+# Use "manual" target tag to skip rules in the wildcard expansion
+
+pkg_tar(
+    name = "configs_tar",
+    extension = "tar.gz",
+    files = glob([
+        "**/*.yml",
+    ]),
+    mode = "0755",
+    package_dir = "/etc/opt/mixer",
+    strip_prefix = "./",
+    tags = ["manual"],
+    visibility = ["//docker:__pkg__"],
+)
diff --git a/config/testdata/configroot/scopes/global/adapters.yml b/config/testdata/configroot/scopes/global/adapters.yml
new file mode 100644
index 0000000..52618bf
--- /dev/null
+++ b/config/testdata/configroot/scopes/global/adapters.yml
@@ -0,0 +1,46 @@
+subject: global
+adapters:
+  - name: default
+    kind: quotas
+    impl: memQuota
+    params:
+  - name: default
+    impl: stdioLogger
+    params:
+      logStream: STDERR
+  - name: prometheus
+    kind: metrics
+    impl: prometheus
+    params:
+  - name: default
+    impl: denyChecker
+  - name: default
+    kind: attributes
+    impl: kubernetes
+      # when running from mixer root, use the following config after adding a
+      # symbolic link to a kubernetes config file via:
+      #      
+      # $ ln -s ~/.kube/config adapter/kubernetes/kubeconfig
+      #
+      # kubeconfig_path: "adapter/kubernetes/kubeconfig"
+  - name: apigeeAnalytics
+    kind: access-logs
+    impl: apigeeReport
+    params:
+      organization: {{org_name}}
+      environment: {{env}}
+      key: {{microgateway_key}}
+      secret: {{microgateway_secret}}
+      collectionURL: https://edgemicroservices-us-east-1.apigee.net/edgemicro
+  - name: apigeeAttributes
+    kind: attributes
+    impl: apigeeKeyAttributes
+    params:
+      organization: {{org_name}}
+      environment: {{env}}
+  - name: authorizationChecker
+    kind: lists
+    impl: genericListChecker
+    params:
+      listEntries:
+      - "true"
diff --git a/config/testdata/configroot/scopes/global/descriptors.yml b/config/testdata/configroot/scopes/global/descriptors.yml
new file mode 100644
index 0000000..7e29fba
--- /dev/null
+++ b/config/testdata/configroot/scopes/global/descriptors.yml
@@ -0,0 +1,239 @@
+subject: namespace:ns
+revision: "2022"
+manifests:
+  - name: kubernetes
+    revision: "1"
+    attributes:
+      source.ip:
+        valueType: IP_ADDRESS
+      source.labels:
+        valueType: STRING_MAP
+      source.name:
+        valueType: STRING
+      source.namespace:
+        valueType: STRING
+      source.service:
+        valueType: STRING
+      source.serviceAccount:
+        valueType: STRING
+      target.ip:
+        valueType: IP_ADDRESS
+      target.labels:
+        valueType: STRING_MAP
+      target.name:
+        valueType: STRING
+      target.namespace:
+        valueType: STRING
+      target.service:
+        valueType: STRING
+      target.serviceAccount:
+        valueType: STRING
+  - name: istio-proxy
+    revision: "1"
+    attributes:
+      origin.ip:
+        valueType: IP_ADDRESS
+      origin.uid:
+        valueType: STRING
+      origin.user:
+        valueType: STRING
+      request.headers:
+        valueType: STRING_MAP
+      request.id:
+        valueType: STRING
+      request.host:
+        valueType: STRING
+      request.method:
+        valueType: STRING
+      request.path:
+        valueType: STRING
+      request.reason:
+        valueType: STRING
+      request.referer:
+        valueType: STRING
+      request.scheme:
+        valueType: STRING
+      request.size:
+        valueType: INT64
+      request.time:
+        valueType: TIMESTAMP
+      request.useragent:
+        valueType: STRING
+      response.code:
+        valueType: INT64
+      response.duration:
+        valueType: DURATION
+      response.headers:
+        valueType: STRING_MAP
+      response.latency:
+        valueType: DURATION
+      response.size:
+        valueType: INT64
+      response.time:
+        valueType: TIMESTAMP
+      source.uid:
+        valueType: STRING
+      target.uid:
+        valueType: STRING
+      proxy.name:
+        valueType: STRING
+      proxy.revision:
+        valueType: INT64
+      authorization.success:
+        valueType: BOOL
+      authorization.success.string:
+        valueType: STRING
+      authorization.client.id:
+        valueType: STRING
+      authorization.application.name:
+        valueType: STRING
+      authorization.apiproduct.name:
+        valueType: STRING
+      # DEPRECATED, to be removed. Use request.useragent instead.
+      request.user-agent:
+        valueType: STRING
+  - name: apigeeKeyAttributes
+    revision: "1"
+    attributes:
+      apiKey:
+        valueType: STRING
+      requestPath:
+        valueType: STRING
+      success:
+        valueType: BOOL
+      successString:
+        valueType: STRING
+      clientID:
+        valueType: STRING
+      applicationName:
+        valueType: STRING
+      apiProduct:
+        valueType: STRING
+# Enums as struct fields can be symbolic names.
+# However enums inside maps *cannot* be symbolic names.
+metrics:
+  - name: request_count
+    kind: COUNTER
+    value: INT64
+    description: request count by source, target, service, and code
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+  - name: request_duration
+    kind: DISTRIBUTION
+    value: DURATION
+    description: request duration by source, target, and service
+    buckets:
+      explicit_buckets:
+        bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
+    # Examples of other possible bucket configurations:
+    #      linear_buckets:
+    #         num_finite_buckets: 10
+    #         offset: 0.001
+    #         width: 0.1
+    #      exponential_buckets:
+    #        num_finite_buckets: 15
+    #        scale: 0.001
+    #        growth_factor: 4
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+  - name: request_size
+    kind: DISTRIBUTION
+    value: INT64
+    description: request size by source, target, and service
+    buckets:
+      exponentialBuckets:
+        numFiniteBuckets: 8
+        scale: 1
+        growthFactor: 10
+    # Examples of other possible bucket configurations:
+    #      explicit_buckets:
+    #         bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
+    #      linear_buckets:
+    #         num_finite_buckets: 10
+    #         offset: 0.001
+    #         width: 0.1
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+  - name: response_size
+    kind: DISTRIBUTION
+    value: INT64
+    description: response size by source, target, and service
+    buckets:
+      exponentialBuckets:
+        numFiniteBuckets: 8
+        scale: 1
+        growthFactor: 10
+    # Examples of other possible bucket configurations:
+    #      explicitBuckets:
+    #         bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
+    #      linearBuckets:
+    #         numFiniteBuckets: 10
+    #         offset: 0.001
+    #         width: 0.1
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+quotas:
+  - name: RequestCount
+    rate_limit: true
+logs:
+  - name: accesslog.common
+    display_name: Apache Common Log Format
+    payload_format: TEXT
+    log_template: '{{or (.originIp) "-"}} - {{or (.sourceUser) "-"}} [{{or (.timestamp.Format "02/Jan/2006:15:04:05 -0700") "-"}}] "{{or (.method) "-"}} {{or (.url) "-"}} {{or (.protocol) "-"}}" {{or (.responseCode) "-"}} {{or (.responseSize) "-"}}'
+    labels:
+      originIp: 6 # IP_ADDRESS
+      sourceUser: 1 # STRING
+      timestamp: 5 # TIMESTAMP
+      method: 1 # STRING
+      url: 1 # STRING
+      protocol: 1 # STRING
+      responseCode: 2 # INT64
+      responseSize: 2 # INT64
+  - name: accesslog.combined
+    display_name: Apache Combined Log Format
+    payload_format: TEXT
+    log_template: '{{or (.originIp) "-"}} - {{or (.sourceUser) "-"}} [{{or (.timestamp.Format "02/Jan/2006:15:04:05 -0700") "-"}}] "{{or (.method) "-"}} {{or (.url) "-"}} {{or (.protocol) "-"}}" {{or (.responseCode) "-"}} {{or (.responseSize) "-"}} {{or (.referer) "-"}} {{or (.userAgent) "-"}}'
+    labels:
+      originIp: 6 # IP_ADDRESS
+      sourceUser: 1 # STRING
+      timestamp: 5 # TIMESTAMP
+      method: 1 # STRING
+      url: 1 # STRING
+      protocol: 1 # STRING
+      responseCode: 2 # INT64
+      responseSize: 2 # INT64
+      referer: 1 # STRING
+      userAgent: 1 # STRING
+  - name: accesslog.apigee
+    payload_format: TEXT
+    log_template: 'UNUSED'
+    labels:
+      sourceIP: 6 # IP_ADDRESS
+      urlPath: 1
+      hostHeader: 1
+      httpMethod: 1
+      userAgent: 1
+      requestTime: 5
+      responseTime: 5
+      responseCode: 2
+      proxyName: 1
+      proxyRevision: 2
+      clientID: 1
+      applicationName: 1
+      apiProduct: 1
diff --git a/config/testdata/configroot/scopes/global/subjects/global/rules.yml b/config/testdata/configroot/scopes/global/subjects/global/rules.yml
new file mode 100644
index 0000000..3a22606
--- /dev/null
+++ b/config/testdata/configroot/scopes/global/subjects/global/rules.yml
@@ -0,0 +1,79 @@
+subject: namespace:ns
+revision: "2022"
+rules:
+- selector: # must be empty for preprocessing adapters
+  aspects:
+  # when running local without a kubeconfig file specified in globalconfig,
+  # this aspect should be commented out. It is only needed when the attributes
+  # it produces are needed elsewhere in the config.
+  - kind: attributes
+    params:
+      input_expressions:
+        sourceUID: source.uid | ""
+        targetUID: target.uid | ""
+        originUID: origin.uid | ""
+        targetService: request.headers["authority"] | request.host | ""
+      attribute_bindings:
+        source.ip: sourcePodIp
+        source.labels: sourceLabels
+        source.name: sourcePodName
+        source.namespace: sourceNamespace
+        source.service: sourceService
+        source.serviceAccount: sourceServiceAccountName
+        target.ip: targetPodIp
+        target.labels: targetLabels
+        target.name: targetPodName
+        target.namespace: targetNamespace
+        target.service: targetService
+        target.serviceAccount: targetServiceAccountName
+  - kind: quotas
+    params:
+      quotas:
+      - descriptorName: RequestCount
+        maxAmount: 5000
+        expiration: 1s
+  - kind: metrics
+    adapter: prometheus
+    params:
+      metrics:
+      - descriptor_name: request_count
+        # we want to increment this counter by 1 for each unique (source, target, service, method, response_code) tuple
+        value: "1"
+        labels:
+          source: source.labels["app"] | "unknown"
+          target: target.service | "unknown"
+          service: target.labels["app"] | "unknown"
+          method: request.path | "unknown"
+          response_code: response.code | 200
+      - descriptor_name:  request_duration
+        value: response.latency | response.duration | "0ms"
+        labels:
+          source: source.labels["app"] | "unknown"
+          target: target.service | "unknown"
+          service: target.labels["app"] | "unknown"
+          method: request.path | "unknown"
+          response_code: response.code | 200
+  - kind: access-logs
+    params:
+      logName: access_log
+      log:
+        descriptor_name: accesslog.common
+        template_expressions:
+           originIp: origin.ip
+           sourceUser: origin.user
+           timestamp: request.time
+           method: request.method
+           url: request.path
+           protocol: request.scheme
+           responseCode: response.code
+           responseSize: response.size
+        labels:
+           originIp: origin.ip
+           sourceUser: origin.user
+           timestamp: request.time
+           method: request.method
+           url: request.path
+           protocol: request.scheme
+           responseCode: response.code
+           responseSize: response.size
+
diff --git a/config/testdata/configroot/scopes/global/subjects/myservice.ns.svc/rules.yml b/config/testdata/configroot/scopes/global/subjects/myservice.ns.svc/rules.yml
new file mode 100644
index 0000000..386fbfd
--- /dev/null
+++ b/config/testdata/configroot/scopes/global/subjects/myservice.ns.svc/rules.yml
@@ -0,0 +1,6 @@
+subject: namespace:ns
+revision: "2022"
+rules:
+- aspects:
+  - kind: denials
+ 
diff --git a/config/testdata/globalconfig.yml b/config/testdata/globalconfig.yml
new file mode 100644
index 0000000..d6ffe6a
--- /dev/null
+++ b/config/testdata/globalconfig.yml
@@ -0,0 +1,149 @@
+subject: namespace:ns
+revision: "2022"
+adapters:
+  - name: default
+    kind: quotas
+    impl: memQuota
+    params:
+  - name: default
+    impl: stdioLogger
+    params:
+      logStream: STDERR
+  - name: prometheus
+    kind: metrics
+    impl: prometheus
+    params:
+  - name: default
+    impl: denyChecker
+  - name: default
+    kind: attributes
+    impl: kubernetes
+    params:
+      # when running from mixer root, use the following config after adding a
+      # symbolic link to a kubernetes config file via:
+      #
+      # $ ln -s ~/.kube/config adapter/kubernetes/kubeconfig
+      #
+      # kubeconfig_path: "adapter/kubernetes/kubeconfig"
+manifests:
+  - name: istio-proxy
+    revision: "1"
+    attributes:
+    - name: source.uid
+      value_type: STRING
+    - name: target.uid
+      value_type: STRING
+    - name: origin.uid
+      value_type: STRING
+    - name: source.name
+      value_type: STRING
+    - name: source.labels
+      value_type: STRING_MAP
+    - name: target.name
+      value_type: STRING
+    - name: target.service
+      value_type: STRING
+    - name: source.namespace
+      value_type: STRING
+    - name: target.namespace
+      value_type: STRING
+    - name: source.service
+      value_type: STRING
+    - name: target.service
+      value_type: STRING
+    - name: source.ip
+      value_type: IP_ADDRESS
+    - name: origin.ip
+      value_type: IP_ADDRESS
+    - name: origin.user
+      value_type: STRING
+    - name: source.serviceAccount
+      value_type: STRING
+    - name: request.time
+      value_type: TIMESTAMP
+    - name: request.method
+      value_type: STRING
+    - name: request.path
+      value_type: STRING
+    - name: request.host
+      value_type: STRING
+    - name: request.headers
+      value_type: STRING_MAP
+    - name: request.scheme
+      value_type: STRING
+    - name: response.size
+      value_type: INT64
+    - name: response.code
+      value_type: INT64
+    - name: response.duration
+      value_type: DURATION
+    # TODO: we really need to remove these, they're not part of the attribute vocab.
+    - name: api.name
+      value_type: STRING
+    - name: api.method
+      value_type: STRING
+# Enums as struct fields can be symbolic names.
+# However enums inside maps *cannot* be symbolic names.
+metrics:
+  - name: request_count
+    kind: COUNTER
+    value: INT64
+    description: request count by source, target, service, and code
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+  - name: request_latency
+    kind: DISTRIBUTION
+    value: DURATION
+    description: request latency by source, target, and service
+    buckets:
+      explicit_buckets:
+        bounds: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
+# Examples of other possible bucket configurations:
+#      linear_buckets:
+#         num_finite_buckets: 10
+#         offset: 0.001
+#         width: 0.1
+#      exponential_buckets:
+#        num_finite_buckets: 15
+#        scale: 0.001
+#        growth_factor: 4
+    labels:
+      source: 1 # STRING
+      target: 1 # STRING
+      service: 1 # STRING
+      method: 1 # STRING
+      response_code: 2 # INT64
+quotas:
+  - name: RequestCount
+    rate_limit: true
+logs:
+  - name: accesslog.common
+    display_name: Apache Common Log Format
+    log_template: '{{or (.originIp) "-"}} - {{or (.sourceUser) "-"}} [{{or (.timestamp.Format "02/Jan/2006:15:04:05 -0700") "-"}}] "{{or (.method) "-"}} {{or (.url) "-"}} {{or (.protocol) "-"}}" {{or (.responseCode) "-"}} {{or (.responseSize) "-"}}'
+    labels:
+      originIp: 6 # IP_ADDRESS
+      sourceUser: 1 # STRING
+      timestamp: 5 # TIMESTAMP
+      method: 1 # STRING
+      url: 1 # STRING
+      protocol: 1 # STRING
+      responseCode: 2 # INT64
+      responseSize: 2 # INT64
+  - name: accesslog.combined
+    display_name: Apache Combined Log Format
+    log_template: '{{or (.originIp) "-"}} - {{or (.sourceUser) "-"}} [{{or (.timestamp.Format "02/Jan/2006:15:04:05 -0700") "-"}}] "{{or (.method) "-"}} {{or (.url) "-"}} {{or (.protocol) "-"}}" {{or (.responseCode) "-"}} {{or (.responseSize) "-"}} {{or (.referer) "-"}} {{or (.userAgent) "-"}}'
+    labels:
+      originIp: 6 # IP_ADDRESS
+      sourceUser: 1 # STRING
+      timestamp: 5 # TIMESTAMP
+      method: 1 # STRING
+      url: 1 # STRING
+      protocol: 1 # STRING
+      responseCode: 2 # INT64
+      responseSize: 2 # INT64
+      referer: 1 # STRING
+      userAgent: 1 # STRING
diff --git a/config/testdata/serviceconfig.yml b/config/testdata/serviceconfig.yml
new file mode 100644
index 0000000..6ff4022
--- /dev/null
+++ b/config/testdata/serviceconfig.yml
@@ -0,0 +1,73 @@
+subject: namespace:ns
+revision: "2022"
+rules:
+- selector: # must be empty for preprocessing adapters
+  aspects:
+  # when running local without a kubeconfig file specified in globalconfig,
+  # this aspect should be commented out. It is only needed when the attributes
+  # it produces are needed elsewhere in the config.
+  - kind: attributes
+    params:
+      input_expressions:
+        sourceUID: source.uid | ""
+        targetUID: target.uid | ""
+        originUID: origin.uid | ""
+        targetService: request.headers["authority"] | request.host | ""
+      attribute_bindings:
+        source.ip: sourcePodIp
+        source.service: sourceService
+        source.name: sourcePodName
+        source.namespace: sourceNamespace
+        source.labels: sourceLabels
+        source.serviceAccount: sourceServiceAccountName
+        target.service: targetService
+  - kind: quotas
+    params:
+      quotas:
+      - descriptor_name: RequestCount
+        max_amount: 5
+        expiration: 1s
+  - kind: metrics
+    adapter: prometheus
+    params:
+      metrics:
+      - descriptor_name: request_count
+        # we want to increment this counter by 1 for each unique (source, target, service, method, response_code) tuple
+        value: "1"
+        labels:
+          source: source.name | "unknown"
+          target: target.name | "unknown"
+          service: api.name | "unknown"
+          method: api.method | "unknown"
+          response_code: response.code | 200
+      - descriptor_name:  request_latency
+        value: response.duration | "0ms"
+        labels:
+          source: source.name | "unknown"
+          target: target.name | "unknown"
+          service: api.name | "unknown"
+          method: api.method | "unknown"
+          response_code: response.code | 200
+  - kind: access-logs
+    params:
+      logName: access_log
+      log:
+        descriptor_name: accesslog.common
+        template_expressions:
+           originIp: origin.ip
+           sourceUser: origin.user
+           timestamp: request.time
+           method: request.method
+           url: request.path
+           protocol: request.scheme
+           responseCode: response.code
+           responseSize: response.size
+        labels:
+           originIp: origin.ip
+           sourceUser: origin.user
+           timestamp: request.time
+           method: request.method
+           url: request.path
+           protocol: request.scheme
+           responseCode: response.code
+           responseSize: response.size
diff --git a/demo/apps/bookinfo/BUILD b/demo/apps/bookinfo/BUILD
new file mode 100644
index 0000000..5ce7a3b
--- /dev/null
+++ b/demo/apps/bookinfo/BUILD
@@ -0,0 +1,16 @@
+filegroup(
+    name = "bookinfo",
+    srcs = [
+        "bookinfo.yaml",
+        "mixer-rule-additional-telemetry.yaml",
+        "mixer-rule-empty-rule.yaml",
+        "mixer-rule-ratings-denial.yaml",
+        "mixer-rule-ratings-ratelimit.yaml",
+        "route-rule-all-v1.yaml",
+        "route-rule-delay.yaml",
+        "route-rule-reviews-50-v3.yaml",
+        "route-rule-reviews-test-v2.yaml",
+        "route-rule-reviews-v2-v3.yaml",
+    ],
+    visibility = ["//visibility:public"],
+)
diff --git a/demo/apps/bookinfo/README.md b/demo/apps/bookinfo/README.md
new file mode 100644
index 0000000..3c72e26
--- /dev/null
+++ b/demo/apps/bookinfo/README.md
@@ -0,0 +1,2 @@
+See the [Bookinfo demo](https://istio.io/docs/samples/bookinfo.html) in Istio
+docs for instructions on how to run this demo application.
diff --git a/demo/apps/bookinfo/bookinfo.yaml b/demo/apps/bookinfo/bookinfo.yaml
new file mode 100644
index 0000000..dec1645
--- /dev/null
+++ b/demo/apps/bookinfo/bookinfo.yaml
@@ -0,0 +1,222 @@
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+##################################################################################################
+# Details service
+##################################################################################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: details
+  labels:
+    app: details
+spec:
+  ports:
+  - port: 9080
+    name: http
+  selector:
+    app: details
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: details-v1
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: details
+        version: v1
+    spec:
+      containers:
+      - name: details
+        image: istio/examples-bookinfo-details-v1
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+##################################################################################################
+# Ratings service
+##################################################################################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: ratings
+  labels:
+    app: ratings
+spec:
+  ports:
+  - port: 9080
+    name: http
+  selector:
+    app: ratings
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: ratings-v1
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: ratings
+        version: v1
+    spec:
+      containers:
+      - name: ratings
+        image: istio/examples-bookinfo-ratings-v1
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+##################################################################################################
+# Reviews service
+##################################################################################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: reviews
+  labels:
+    app: reviews
+spec:
+  ports:
+  - port: 9080
+    name: http
+  selector:
+    app: reviews
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: reviews-v1
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: reviews
+        version: v1
+    spec:
+      containers:
+      - name: reviews
+        image: istio/examples-bookinfo-reviews-v1
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: reviews-v2
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: reviews
+        version: v2
+    spec:
+      containers:
+      - name: reviews
+        image: istio/examples-bookinfo-reviews-v2
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: reviews-v3
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: reviews
+        version: v3
+    spec:
+      containers:
+      - name: reviews
+        image: istio/examples-bookinfo-reviews-v3
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+##################################################################################################
+# Productpage service
+##################################################################################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: productpage
+  labels:
+    app: productpage
+spec:
+  ports:
+  - port: 9080
+    name: http
+  selector:
+    app: productpage
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: productpage-v1
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: productpage
+        version: v1
+    spec:
+      containers:
+      - name: productpage
+        image: istio/examples-bookinfo-productpage-v1
+        imagePullPolicy: IfNotPresent
+        ports:
+        - containerPort: 9080
+---
+###########################################################################
+# Ingress resource (gateway)
+##########################################################################
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  name: gateway
+  annotations:
+    kubernetes.io/ingress.class: "istio"
+spec:
+  rules:
+  - http:
+      paths:
+      - path: /productpage
+        backend:
+          serviceName: productpage
+          servicePort: 9080
+      - path: /login
+        backend:
+          serviceName: productpage
+          servicePort: 9080
+      - path: /logout
+        backend:
+          serviceName: productpage
+          servicePort: 9080
+      - path: /ratings
+        backend:
+          serviceName: ratings
+          servicePort: 9080
+---
diff --git a/demo/apps/bookinfo/cleanup.sh b/demo/apps/bookinfo/cleanup.sh
new file mode 100755
index 0000000..533db2d
--- /dev/null
+++ b/demo/apps/bookinfo/cleanup.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+istioctl delete route-rule productpage-default
+istioctl delete route-rule reviews-default
+istioctl delete route-rule ratings-default
+istioctl delete route-rule details-default
+istioctl delete route-rule reviews-test-v2
+istioctl delete route-rule ratings-test-delay
+#istioctl delete mixer-rule ratings-ratelimit
+
+kubectl delete -f $SCRIPTDIR/bookinfo.yaml
diff --git a/demo/apps/bookinfo/destination-ratings-test-delay.yaml b/demo/apps/bookinfo/destination-ratings-test-delay.yaml
new file mode 100644
index 0000000..2d39120
--- /dev/null
+++ b/demo/apps/bookinfo/destination-ratings-test-delay.yaml
@@ -0,0 +1,16 @@
+type: route-rule
+name: ratings-test-delay
+spec:
+  destination: ratings.default.svc.cluster.local
+  precedence: 2
+  match:
+    httpHeaders:
+      cookie:
+        regex: "^(.*?;)?(user=jason)(;.*)?$"
+  route:
+  - tags:
+      version: v1
+  httpFault:
+    delay:
+      percent: 100
+      fixedDelay: 7s
diff --git a/demo/apps/bookinfo/mixer-rule-additional-telemetry.yaml b/demo/apps/bookinfo/mixer-rule-additional-telemetry.yaml
new file mode 100644
index 0000000..17be581
--- /dev/null
+++ b/demo/apps/bookinfo/mixer-rule-additional-telemetry.yaml
@@ -0,0 +1,42 @@
+revision: "1"
+rules:
+- aspects:
+  - adapter: prometheus
+    kind: metrics
+    params:
+      metrics:
+      - descriptor_name: response_size
+        value: response.size | 0
+        labels:
+          source: source.service | "unknown"
+          target: target.service | "unknown"
+          service: target.labels["app"] | "unknown"
+          method: request.path | "unknown"
+          response_code: response.code | 200
+  - kind: access-logs
+    params:
+      logName: combined_log
+      log:
+        descriptor_name: accesslog.combined
+        template_expressions:
+          originIp: origin.ip
+          sourceUser: origin.user
+          timestamp: request.time
+          method: request.method
+          url: request.path
+          protocol: request.scheme
+          responseCode: response.code
+          responseSize: response.size
+          referer: request.referer
+          userAgent: request.headers["user-agent"]
+        labels:
+          originIp: origin.ip
+          sourceUser: origin.user
+          timestamp: request.time
+          method: request.method
+          url: request.path
+          protocol: request.scheme
+          responseCode: response.code
+          responseSize: response.size
+          referer: request.referer
+          userAgent: request.headers["user-agent"]
diff --git a/demo/apps/bookinfo/mixer-rule-empty-rule.yaml b/demo/apps/bookinfo/mixer-rule-empty-rule.yaml
new file mode 100644
index 0000000..59b07e9
--- /dev/null
+++ b/demo/apps/bookinfo/mixer-rule-empty-rule.yaml
@@ -0,0 +1,2 @@
+revision: "2"
+rules:
diff --git a/demo/apps/bookinfo/mixer-rule-ratings-denial.yaml b/demo/apps/bookinfo/mixer-rule-ratings-denial.yaml
new file mode 100644
index 0000000..d6e5ba7
--- /dev/null
+++ b/demo/apps/bookinfo/mixer-rule-ratings-denial.yaml
@@ -0,0 +1,4 @@
+rules:
+  - selector: source.labels["app"]=="reviews" && source.labels["version"] == "v3"
+    aspects:
+    - kind: denials
diff --git a/demo/apps/bookinfo/mixer-rule-ratings-ratelimit.yaml b/demo/apps/bookinfo/mixer-rule-ratings-ratelimit.yaml
new file mode 100644
index 0000000..4284de0
--- /dev/null
+++ b/demo/apps/bookinfo/mixer-rule-ratings-ratelimit.yaml
@@ -0,0 +1,8 @@
+rules:
+- aspects:
+  - kind: quotas
+    params:
+      quotas:
+      - descriptorName: RequestCount
+        maxAmount: 5
+        expiration: 1s
diff --git a/demo/apps/bookinfo/route-rule-all-v1.yaml b/demo/apps/bookinfo/route-rule-all-v1.yaml
new file mode 100644
index 0000000..b136ee9
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-all-v1.yaml
@@ -0,0 +1,35 @@
+type: route-rule
+name: productpage-default
+spec:
+  destination: productpage.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v1
+---
+type: route-rule
+name: reviews-default
+spec:
+  destination: reviews.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v1
+---
+type: route-rule
+name: ratings-default
+spec:
+  destination: ratings.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v1
+---
+type: route-rule
+name: details-default
+spec:
+  destination: details.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v1
diff --git a/demo/apps/bookinfo/route-rule-delay.yaml b/demo/apps/bookinfo/route-rule-delay.yaml
new file mode 100644
index 0000000..2bfecb2
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-delay.yaml
@@ -0,0 +1,16 @@
+type: route-rule
+name: ratings-test-delay
+spec:
+  destination: ratings.default.svc.cluster.local
+  precedence: 2
+  match:
+    httpHeaders:
+      cookie:
+        regex: "^(.*?;)?(user=test-user)(;.*)?$"
+  route:
+  - tags:
+      version: v1
+  httpFault:
+    delay:
+      percent: 100
+      fixedDelay: 7s
diff --git a/demo/apps/bookinfo/route-rule-reviews-50-v3.yaml b/demo/apps/bookinfo/route-rule-reviews-50-v3.yaml
new file mode 100644
index 0000000..a517ec2
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-reviews-50-v3.yaml
@@ -0,0 +1,12 @@
+type: route-rule
+name: reviews-default
+spec:
+  destination: reviews.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v1
+    weight: 50
+  - tags:
+      version: v3
+    weight: 50
diff --git a/demo/apps/bookinfo/route-rule-reviews-test-v2.yaml b/demo/apps/bookinfo/route-rule-reviews-test-v2.yaml
new file mode 100644
index 0000000..775cc83
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-reviews-test-v2.yaml
@@ -0,0 +1,12 @@
+type: route-rule
+name: reviews-test-v2
+spec:
+  destination: reviews.default.svc.cluster.local
+  precedence: 2
+  match:
+    httpHeaders:
+      cookie:
+        regex: "^(.*?;)?(user=jason)(;.*)?$"
+  route:
+  - tags:
+      version: v2
diff --git a/demo/apps/bookinfo/route-rule-reviews-v2-v3.yaml b/demo/apps/bookinfo/route-rule-reviews-v2-v3.yaml
new file mode 100644
index 0000000..4be24e0
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-reviews-v2-v3.yaml
@@ -0,0 +1,12 @@
+type: route-rule
+name: reviews-default
+spec:
+  destination: reviews.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v2
+    weight: 50
+  - tags:
+      version: v3
+    weight: 50
diff --git a/demo/apps/bookinfo/route-rule-reviews-v3.yaml b/demo/apps/bookinfo/route-rule-reviews-v3.yaml
new file mode 100644
index 0000000..d238270
--- /dev/null
+++ b/demo/apps/bookinfo/route-rule-reviews-v3.yaml
@@ -0,0 +1,9 @@
+type: route-rule
+name: reviews-default
+spec:
+  destination: reviews.default.svc.cluster.local
+  precedence: 1
+  route:
+  - tags:
+      version: v3
+    weight: 100
diff --git a/demo/apps/bookinfo/src/build-services.sh b/demo/apps/bookinfo/src/build-services.sh
new file mode 100755
index 0000000..9dcb95f
--- /dev/null
+++ b/demo/apps/bookinfo/src/build-services.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+set -o errexit
+
+SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+
+pushd $SCRIPTDIR/productpage
+  docker build -t istio/examples-bookinfo-productpage-v1 .
+popd
+
+pushd $SCRIPTDIR/details
+  docker build -t istio/examples-bookinfo-details-v1 .
+popd
+
+pushd $SCRIPTDIR/reviews
+  #java build the app.
+  docker run --rm -v `pwd`:/usr/bin/app:rw niaquinto/gradle clean build
+  pushd reviews-wlpcfg
+    #plain build -- no ratings
+    docker build -t istio/examples-bookinfo-reviews-v1 --build-arg service_version=v1 .
+    #with ratings black stars
+    docker build -t istio/examples-bookinfo-reviews-v2 --build-arg service_version=v2 --build-arg enable_ratings=true .
+    #with ratings red stars
+    docker build -t istio/examples-bookinfo-reviews-v3 --build-arg service_version=v3 --build-arg enable_ratings=true --build-arg star_color=red .
+  popd
+popd
+
+pushd $SCRIPTDIR/ratings
+  docker build -t istio/examples-bookinfo-ratings-v1 .
+popd
diff --git a/demo/apps/bookinfo/src/details/Dockerfile b/demo/apps/bookinfo/src/details/Dockerfile
new file mode 100644
index 0000000..b721e3e
--- /dev/null
+++ b/demo/apps/bookinfo/src/details/Dockerfile
@@ -0,0 +1,22 @@
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+FROM ruby:2.3
+
+RUN mkdir -p /opt/microservices
+COPY . /opt/microservices/
+EXPOSE 9080
+WORKDIR /opt/microservices
+
+CMD ruby details.rb 9080
diff --git a/demo/apps/bookinfo/src/details/details.rb b/demo/apps/bookinfo/src/details/details.rb
new file mode 100755
index 0000000..a7ba941
--- /dev/null
+++ b/demo/apps/bookinfo/src/details/details.rb
@@ -0,0 +1,83 @@
+#!/usr/bin/ruby
+#
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+require 'webrick'
+
+if ARGV.length < 1 then
+    puts "usage: #{$PROGRAM_NAME} port"
+    exit(-1)
+end
+
+port = Integer(ARGV[0])
+
+server = WEBrick::HTTPServer.new :BindAddress => '0.0.0.0', :Port => port
+
+trap 'INT' do server.shutdown end
+
+details_resp = '
+<h4 class="text-center text-primary">Book Details</h4>
+<dl>
+<dt>Paperback:</dt>200 pages
+<dt>Publisher:</dt> PublisherA
+<dt>Language:</dt>English
+<dt>ISBN-10:</dt>1234567890
+<dt>ISBN-13:</dt>123-1234567980
+</dl>
+'
+
+server.mount_proc '/health' do |req, res|
+    res.status = 200
+    res.body = 'Details is healthy'
+    res['Content-Type'] = 'text/html'
+end
+
+server.mount_proc '/details' do |req, res|
+    res.body = details_resp
+    res['Content-Type'] = 'text/html'
+end
+
+server.mount_proc '/' do |req, res|
+  res.body = '
+    <html>
+    <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+
+    <!-- Latest compiled and minified CSS -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
+
+    <!-- Optional theme -->
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
+
+    <!-- Latest compiled and minified JavaScript -->
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+
+    <!-- Latest compiled and minified JavaScript -->
+    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+
+    </head>
+    <title>Book details service</title>
+    <body>
+    <p><h2>Hello! This is the book details service. My content is</h2></p>
+    <div>%s</div>
+    </body>
+    </html>
+  ' % [details_resp]
+  res['Content-Type'] = 'text/html'
+end
+
+server.start
diff --git a/demo/apps/bookinfo/src/productpage/Dockerfile b/demo/apps/bookinfo/src/productpage/Dockerfile
new file mode 100644
index 0000000..bf52623
--- /dev/null
+++ b/demo/apps/bookinfo/src/productpage/Dockerfile
@@ -0,0 +1,21 @@
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+FROM python:2-onbuild
+
+RUN mkdir -p /opt/microservices
+COPY . /opt/microservices/
+EXPOSE 9080
+WORKDIR /opt/microservices
+CMD python productpage.py 9080
diff --git a/demo/apps/bookinfo/src/productpage/productpage.py b/demo/apps/bookinfo/src/productpage/productpage.py
new file mode 100644
index 0000000..52cde6b
--- /dev/null
+++ b/demo/apps/bookinfo/src/productpage/productpage.py
@@ -0,0 +1,181 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+
+from flask import Flask, request, render_template, redirect, url_for
+import simplejson as json
+import requests
+import sys
+from json2html import *
+import logging
+import requests
+
+# These two lines enable debugging at httplib level (requests->urllib3->http.client)
+# You will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
+# The only thing missing will be the response.body which is not logged.
+try:
+    import http.client as http_client
+except ImportError:
+    # Python 2
+    import httplib as http_client
+http_client.HTTPConnection.debuglevel = 1
+
+app = Flask(__name__)
+logging.basicConfig(filename='microservice.log',filemode='w',level=logging.DEBUG)
+requests_log = logging.getLogger("requests.packages.urllib3")
+requests_log.setLevel(logging.DEBUG)
+requests_log.propagate = True
+app.logger.addHandler(logging.StreamHandler(sys.stdout))
+app.logger.setLevel(logging.DEBUG)
+
+from flask_bootstrap import Bootstrap
+Bootstrap(app)
+
+details = {
+    "name" : "http://details:9080",
+    "endpoint" : "details",
+    "children" : []
+}
+
+ratings = {
+    "name" : "http://ratings:9080",
+    "endpoint" : "ratings",
+    "children" : []
+}
+
+reviews = {
+    "name" : "http://reviews:9080",
+    "endpoint" : "reviews",
+    "children" : [ratings]
+}
+
+productpage = {
+    "name" : "http://productpage:9080",
+    "endpoint" : "details",
+    "children" : [details, reviews]
+}
+
+service_dict = {
+    "productpage" : productpage,
+    "details" : details,
+    "reviews" : reviews,
+}
+
+def getForwardHeaders(request):
+    headers = {}
+
+    user_cookie = request.cookies.get("user")
+    if user_cookie:
+        headers['Cookie'] = 'user=' + user_cookie
+
+    incoming_headers = [ 'x-request-id',
+                         'x-b3-traceid',
+                         'x-b3-spanid',
+                         'x-b3-parentspanid',
+                         'x-b3-sampled',
+                         'x-b3-flags',
+                         'x-ot-span-context'
+    ]
+
+    for ihdr in incoming_headers:
+        val = request.headers.get(ihdr)
+        if val is not None:
+            headers[ihdr] = val
+            #print "incoming: "+ihdr+":"+val
+
+    return headers
+
+@app.route('/')
+@app.route('/index.html')
+def index():
+    """ Display productpage with normal user and test user buttons"""
+    global productpage
+
+    table = json2html.convert(json = json.dumps(productpage),
+                              table_attributes="class=\"table table-condensed table-bordered table-hover\"")
+
+    return render_template('index.html', serviceTable=table)
+
+@app.route('/health')
+def health():
+    return 'Product page is healthy'
+
+@app.route('/login', methods=['POST'])
+def login():
+    user = request.values.get('username')
+    response = app.make_response(redirect(request.referrer))
+    response.set_cookie('user', user)
+    return response
+
+@app.route('/logout', methods=['GET'])
+def logout():
+    response = app.make_response(redirect(request.referrer))
+    response.set_cookie('user', '', expires=0)
+    return response
+
+@app.route('/productpage')
+def front():
+    headers = getForwardHeaders(request)
+    user = request.cookies.get("user", "")
+    bookdetails = getDetails(headers)
+    bookreviews = getReviews(headers)
+    return render_template('productpage.html', details=bookdetails, reviews=bookreviews, user=user)
+
+def getReviews(headers):
+    for i in range(2):
+        try:
+            res = requests.get(reviews['name']+"/"+reviews['endpoint'], headers=headers, timeout=3.0)
+        except:
+            res = None
+
+        if res and res.status_code == 200:
+            return res.text
+
+    return """<h3>Sorry, product reviews are currently unavailable for this book.</h3>"""
+
+
+def getDetails(headers):
+    try:
+        res = requests.get(details['name']+"/"+details['endpoint'], headers=headers, timeout=1.0)
+    except:
+        res = None
+
+    if res and res.status_code == 200:
+        return res.text
+    else:
+        return """<h3>Sorry, product details are currently unavailable for this book.</h3>"""
+
+
+class Writer(object):
+
+    def __init__(self, filename):
+        self.file = open(filename,'w')
+
+    def write(self, data):
+        self.file.write(data)
+        self.file.flush()
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print "usage: %s port" % (sys.argv[0])
+        sys.exit(-1)
+
+    p = int(sys.argv[1])
+    sys.stderr = Writer('stderr.log')
+    sys.stdout = Writer('stdout.log')
+    app.run(host='0.0.0.0', port=p, debug = True, threaded=True)
+
diff --git a/demo/apps/bookinfo/src/productpage/requirements.txt b/demo/apps/bookinfo/src/productpage/requirements.txt
new file mode 100644
index 0000000..619c343
--- /dev/null
+++ b/demo/apps/bookinfo/src/productpage/requirements.txt
@@ -0,0 +1,8 @@
+requests
+flask
+flask_json
+flask_bootstrap
+json2html
+simplejson
+gevent
+
diff --git a/demo/apps/bookinfo/src/productpage/templates/index.html b/demo/apps/bookinfo/src/productpage/templates/index.html
new file mode 100644
index 0000000..6f7d6d2
--- /dev/null
+++ b/demo/apps/bookinfo/src/productpage/templates/index.html
@@ -0,0 +1,32 @@
+{% extends "bootstrap/base.html" %}
+{% block metas %}
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+{% endblock %}
+
+{% block styles %}
+<!-- Latest compiled and minified CSS -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
+
+<!-- Optional theme -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
+{% endblock %}
+{% block scripts %}
+<!-- Latest compiled and minified JavaScript -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+
+<!-- Latest compiled and minified JavaScript -->
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+{% endblock %}
+{% block title %}Simple Bookstore App{% endblock %}
+{% block content %}
+    <p><h3>Hello! This is a simple bookstore application consisting of three services as shown below</h3></p>
+    {% autoescape false %}
+    {{ serviceTable }}
+    {% endautoescape %}
+    <p><h4>Click on one of the links below to auto generate a request to the backend as a real user or a tester
+    </h4></p>
+    <p><a href="/productpage?u=normal">Normal user</a></p>
+    <p><a href="/productpage?u=test">Test user</a></p>
+{% endblock %}
diff --git a/demo/apps/bookinfo/src/productpage/templates/productpage.html b/demo/apps/bookinfo/src/productpage/templates/productpage.html
new file mode 100644
index 0000000..382726a
--- /dev/null
+++ b/demo/apps/bookinfo/src/productpage/templates/productpage.html
@@ -0,0 +1,120 @@
+{% extends "bootstrap/base.html" %}
+{% block metas %}
+<meta charset="utf-8">
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+{% endblock %}
+
+{% block styles %}
+<!-- Latest compiled and minified CSS -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
+
+<!-- Optional theme -->
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
+{% endblock %}
+{% block scripts %}
+<!-- Latest compiled and minified JavaScript -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+
+<!-- Latest compiled and minified JavaScript -->
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
+
+<script type="text/javascript">
+$('#login-modal').on('shown.bs.modal', function () {
+     $('#username').focus();
+});
+</script>
+{% endblock %}
+{% block title %}Simple Bookstore App{% endblock %}
+{% block content %}
+
+<nav class="navbar navbar-inverse navbar-static-top">
+    <div class="container">
+        <div class="navbar-header">
+            <a class="navbar-brand" href="#">BookInfo Sample</a>
+        </div>
+        {% if user: %}
+        <p class="navbar-text navbar-right">
+            <i class="glyphicon glyphicon-user" aria-hidden="true"></i>
+            <span style="padding-left: 5px;">{{ user }}  ( <a href="logout">sign out</a> )</span>
+        </p>
+        {% else %}
+        <button type="button" class="btn btn-default navbar-btn navbar-right" data-toggle="modal" href="#login-modal">Sign in</button>
+        {% endif %}
+    </div>
+</nav>
+
+<!---
+<div class="navbar navbar-inverse navbar-fixed-top">
+  <div class="container">
+    <div class="navbar-header pull-left">
+      <a class="navbar-brand" href="#">Microservices Fabric BookInfo Demo</a>
+    </div>
+    <div class="navbar-header pull-right">
+      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+        <span class="icon-bar"></span>
+      </button>
+    </div>
+    <div class="navbar-collapse collapse">
+{% if user %}
+      <a href="logout"><button type="button" class="btn btn-default navbar-btn pull-right">Sign out</button></a>
+      <p class="navbar-text pull-right">Signed in as {{ user }}</p>
+{% else %}
+      <button type="button" class="btn btn-default navbar-btn pull-right" data-toggle="modal" data-target="#login-modal">Sign in</button>
+{% endif %}
+    </div>
+  </div>
+</div>
+-->
+    
+<div id="login-modal" class="modal fade" role="dialog">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal">&times;</button>
+        <h4 class="modal-title">Please sign in</h4>
+      </div>
+      <div class="modal-body">
+        <form method="post" action='login' name="login_form">
+          <p><input type="text" class="form-control" name="username" id="username" placeholder="User Name"></p>
+          <p><input type="password" class="form-control" name="passwd" placeholder="Password"></p>
+          <p>
+             <button type="submit" class="btn btn-primary">Sign in</button>
+             <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+          </p>
+        </form>
+      </div>
+    </div>
+
+  </div>
+</div>
+
+<div class="container-fluid">
+<div class="row">
+<div class="col-md-12">
+    <h3 class="text-center text-primary">The Comedy of Errors</h3>
+    <p> <a href="https://en.wikipedia.org/wiki/The_Comedy_of_Errors">Wikipedia
+    Summary</a>: The Comedy of Errors is one of <b>William
+    Shakespeare's</b> early plays. It is his shortest and one of his
+    most farcical comedies, with a major part of the humour coming
+    from slapstick and mistaken identity, in addition to puns and word
+    play.</p>
+</div>
+</div>
+
+<div class="row">
+<div class="col-md-6">
+{% autoescape false %}
+{{ details }}
+{% endautoescape %}
+</div>
+<div class="col-md-6">
+{% autoescape false %}
+{{ reviews }}
+{% endautoescape %}
+</div>
+</div>
+</div>
+{% endblock %}
diff --git a/demo/apps/bookinfo/src/ratings/Dockerfile b/demo/apps/bookinfo/src/ratings/Dockerfile
new file mode 100644
index 0000000..45030d7
--- /dev/null
+++ b/demo/apps/bookinfo/src/ratings/Dockerfile
@@ -0,0 +1,18 @@
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+FROM node:4-onbuild
+
+EXPOSE 9080
+CMD node ratings.js 9080
diff --git a/demo/apps/bookinfo/src/ratings/package.json b/demo/apps/bookinfo/src/ratings/package.json
new file mode 100644
index 0000000..40c0984
--- /dev/null
+++ b/demo/apps/bookinfo/src/ratings/package.json
@@ -0,0 +1,8 @@
+{
+  "scripts": {
+    "start": "node ratings.js"
+  },
+  "dependencies": {
+    "httpdispatcher": "1.0.0"
+  }
+}
\ No newline at end of file
diff --git a/demo/apps/bookinfo/src/ratings/ratings.js b/demo/apps/bookinfo/src/ratings/ratings.js
new file mode 100644
index 0000000..2d0cb98
--- /dev/null
+++ b/demo/apps/bookinfo/src/ratings/ratings.js
@@ -0,0 +1,72 @@
+// Copyright 2017 Istio Authors
+//
+//   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.
+
+var http = require('http');
+var dispatcher = require('httpdispatcher');
+
+port = parseInt(process.argv[2]);
+
+var ratingsResponse = {"Reviewer1": 5, "Reviewer2": 4}
+
+dispatcher.onGet("/", function(req, res) {
+    res.writeHead(200)
+    res.end(
+        '<html>' +
+        '<head>' +
+        '<meta charset="utf-8">' +
+        '<meta http-equiv="X-UA-Compatible" content="IE=edge">' +
+        '<meta name="viewport" content="width=device-width, initial-scale=1">' +
+        '<!-- Latest compiled and minified CSS -->' +
+        '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">' +
+        '<!-- Optional theme -->' +
+        '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">' +
+        '<!-- Latest compiled and minified JavaScript -->' +
+        '<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>' +
+        '<!-- Latest compiled and minified JavaScript -->' +
+        '<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>' +
+        '</head>' +
+        '<title>Book ratings service</title>' +
+        '<body>' +
+        '<p><h2>Hello! This is the book ratings service. My content is</h2></p>' +
+        '<div>' + JSON.stringify(ratingsResponse) + '</div>' +
+        '</body>' +
+        '</html>',
+        {"Content-type": "text/html"})
+})
+
+dispatcher.onGet("/ratings", function(req, res) {
+    var json = JSON.stringify(ratingsResponse)
+    res.writeHead(200, {"Content-type": "application/json"})
+    res.end(json)
+})
+
+dispatcher.onGet("/health", function(req, res) {
+    res.writeHead(200, {"Content-type": "text/plain"})
+    res.end("Ratings is healthy")
+})
+
+function handleRequest(request, response){
+    try {
+        console.log(request.method + " " + request.url);
+        dispatcher.dispatch(request, response);
+    } catch(err) {
+        console.log(err);
+    }
+}
+
+var server = http.createServer(handleRequest);
+
+server.listen(port, function(){
+    console.log("Server listening on: http://0.0.0.0:%s", port);
+});
diff --git a/demo/apps/bookinfo/src/reviews/build.gradle b/demo/apps/bookinfo/src/reviews/build.gradle
new file mode 100644
index 0000000..75bf360
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/build.gradle
@@ -0,0 +1,7 @@
+allprojects {
+   group = 'org.istio'
+   version = '1.0'
+   repositories {
+     mavenCentral()
+   }
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/build.gradle b/demo/apps/bookinfo/src/reviews/reviews-application/build.gradle
new file mode 100644
index 0000000..bf0f375
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/build.gradle
@@ -0,0 +1,19 @@
+apply plugin: 'war'
+
+sourceCompatibility = 1.8
+
+repositories {
+  mavenCentral()
+}
+
+dependencies {
+    providedCompile group:'javax.websocket', name:'javax.websocket-api', version:'1.1'
+    providedCompile group:'javax.ws.rs', name:'javax.ws.rs-api', version:'2.0'
+    providedCompile group:'javax.json', name:'javax.json-api', version:'1.0'
+    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
+    providedCompile 'javax.annotation:javax.annotation-api:1.2'
+    providedCompile 'javax.inject:javax.inject:1'
+    providedCompile 'javax.enterprise.concurrent:javax.enterprise.concurrent-api:1.0'
+    providedCompile 'javax.enterprise:cdi-api:1.2'
+    providedCompile 'io.swagger:swagger-annotations:1.5.0'
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/ReviewsApplication.java b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/ReviewsApplication.java
new file mode 100644
index 0000000..9f62285
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/ReviewsApplication.java
@@ -0,0 +1,7 @@
+package application;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+@ApplicationPath("/")
+public class ReviewsApplication extends Application {
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java
new file mode 100644
index 0000000..7379c4b
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/java/application/rest/LibertyRestEndpoint.java
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Istio Authors
+ *
+ * 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 application.rest;
+
+import java.io.StringReader;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.CookieParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.client.ResponseProcessingException;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.Cookie;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("/")
+public class LibertyRestEndpoint extends Application {
+
+    private final static Boolean ratings_enabled = Boolean.valueOf(System.getenv("ENABLE_RATINGS"));
+    private final static String star_color = System.getenv("STAR_COLOR") == null ? "black" : System.getenv("STAR_COLOR");
+    private final static String ratings_service = "http://ratings:9080/ratings";
+
+    private final static String review_resp = ""+
+      "<blockquote>"+
+      "<p>"+
+      "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!"+
+      "</p> <small>Reviewer1 <cite>Affiliation1</cite></small>"+
+      "%s"+
+      "</blockquote>"+
+      "<blockquote>"+
+      "<p>"+
+      "Absolutely fun and entertaining. The play lacks thematic depth when compared "+
+      "to other plays by Shakespeare."+
+      "</p> <small>Reviewer2 <cite>Affiliation2</cite></small>"+
+      "%s"+
+      "</blockquote>";
+
+    private JsonObject getRatings(Cookie user, String xreq, String xtraceid, String xspanid,
+                                  String xparentspanid, String xsampled, String xflags, String xotspan){
+      ClientBuilder cb = ClientBuilder.newBuilder();
+      String timeout = star_color.equals("black") ? "10000" : "2500";
+      cb.property("com.ibm.ws.jaxrs.client.connection.timeout", timeout);
+      cb.property("com.ibm.ws.jaxrs.client.receive.timeout", timeout);
+      Client client = cb.build();
+      WebTarget ratingsTarget = client.target(ratings_service);
+      Invocation.Builder builder = ratingsTarget.request(MediaType.APPLICATION_JSON);
+      if(xreq!=null) {
+        builder.header("x-request-id",xreq);
+      }
+      if(xtraceid!=null) {
+        builder.header("x-b3-traceid",xtraceid);
+      }
+      if(xspanid!=null) {
+        builder.header("x-b3-spanid",xspanid);
+      }
+      if(xparentspanid!=null) {
+        builder.header("x-b3-parentspanid",xparentspanid);
+      }
+      if(xsampled!=null) {
+        builder.header("x-b3-sampled",xsampled);
+      }
+      if(xflags!=null) {
+        builder.header("x-b3-flags",xflags);
+      }
+      if(xotspan!=null) {
+        builder.header("x-ot-span-context",xotspan);
+      }
+      if(user!=null) {
+        builder.cookie(user);
+      }
+      Response r = builder.get();
+      int statusCode = r.getStatusInfo().getStatusCode();
+      if (statusCode == Response.Status.OK.getStatusCode() ) {
+        StringReader stringReader = new StringReader(r.readEntity(String.class));
+        try (JsonReader jsonReader = Json.createReader(stringReader)) {
+           JsonObject j = jsonReader.readObject();
+           JsonObjectBuilder jb = Json.createObjectBuilder();
+           for(String key : j.keySet()){
+             int count = j.getInt(key);
+             String stars = "<font color=\""+ star_color +"\">";
+             for(int i=0; i<count; i++){
+               stars += "<span class=\"glyphicon glyphicon-star\"></span>";
+             }
+             stars += "</font>";
+             if(count<5){
+               for(int i=0; i<(5-count); i++){
+                 stars += "<span class=\"glyphicon glyphicon-star-empty\"></span>";
+               }
+             }
+             jb.add(key,stars);
+           }
+           JsonObject result = jb.build();
+           return result;
+        }
+      }else{
+        System.out.println("Error: unable to contact "+ratings_service+" got status of "+statusCode);
+        return null;
+      }
+    }
+
+    @GET
+    @Path("/health")
+    public Response health() {
+        return Response.ok().type(MediaType.TEXT_HTML_TYPE).entity("Reviews is healthy").build();
+    }
+
+    @GET
+    @Path("/reviews")
+    public Response bookReviews(@CookieParam("user") Cookie user,
+                                @HeaderParam("x-request-id") String xreq,
+                                @HeaderParam("x-b3-traceid") String xtraceid,
+                                @HeaderParam("x-b3-spanid") String xspanid,
+                                @HeaderParam("x-b3-parentspanid") String xparentspanid,
+                                @HeaderParam("x-b3-sampled") String xsampled,
+                                @HeaderParam("x-b3-flags") String xflags,
+                                @HeaderParam("x-ot-span-context") String xotspan) {
+      String r1 = "";
+      String r2 = "";
+
+      if(ratings_enabled){
+        JsonObject ratings = getRatings(user, xreq, xtraceid, xspanid, xparentspanid, xsampled, xflags, xotspan);
+
+        if(ratings!=null){
+          if(ratings.containsKey("Reviewer1")){
+            r1 = ratings.getString("Reviewer1");
+          }
+          if(ratings.containsKey("Reviewer2")){
+            r2 = ratings.getString("Reviewer2");
+          }
+        }else{
+            // return Response.serverError().build();
+            r1 = r2 = "<span class=\"bg-warning\">product ratings not available</span>";
+        }
+      }
+      String replyBody = String.format(review_resp,r1,r2);
+      return Response.ok().type(MediaType.TEXT_HTML_TYPE).entity(replyBody).build();
+    }
+
+    private final static String index = ""+
+    "<html>"+
+    "<head>"+
+    "<meta charset=\"utf-8\">"+
+    "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">"+
+    "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"+
+    "<!-- Latest compiled and minified CSS -->"+
+    "<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css\">"+
+    "<!-- Optional theme -->"+
+    "<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css\">"+
+    "<!-- Latest compiled and minified JavaScript -->"+
+    "<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js\"></script>"+
+    "<!-- Latest compiled and minified JavaScript -->"+
+    "<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js\"></script>"+
+    "</head>"+
+    "<title>Book reviews service</title>"+
+    "<body>"+
+    "<p><h2>Hello! This is the book reviews service. My content is</h2></p>"+
+    "<div>%s</div>"+
+    "<p>Ratings service enabled? %s</p>"+
+    "<p>Star color: %s </p>"+
+    "</body>"+
+    "</html>";
+
+    @GET
+    @Path("/")
+    public String getDefault(){
+      return String.format(index,review_resp,ratings_enabled,star_color);
+    }
+
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/ibm-web-ext.xml b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/ibm-web-ext.xml
new file mode 100644
index 0000000..68c5217
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/ibm-web-ext.xml
@@ -0,0 +1,23 @@
+<!--
+  Copyright (c) 2017 Istio Authors
+
+  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.
+-->
+<!DOCTYPE xml>
+<web-ext
+    xmlns="http://websphere.ibm.com/xml/ns/javaee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-ext_1_0.xsd"
+    version="1.0">
+  <context-root uri="/"/>
+</web-ext>
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/web.xml b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..a3823f1
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+    version="3.1">
+    <display-name>Liberty Project</display-name>
+
+    <welcome-file-list>
+        <welcome-file>index.html</welcome-file>
+    </welcome-file-list>
+</web-app>
\ No newline at end of file
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/index.html b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/index.html
new file mode 100644
index 0000000..d77e51b
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/main/webapp/index.html
Binary files differ
diff --git a/demo/apps/bookinfo/src/reviews/reviews-application/src/test/java/test/TestApplication.java b/demo/apps/bookinfo/src/reviews/reviews-application/src/test/java/test/TestApplication.java
new file mode 100644
index 0000000..16469c7
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-application/src/test/java/test/TestApplication.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Istio Authors
+ *
+ * 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 test;
+
+public class TestApplication {
+
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/Dockerfile b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/Dockerfile
new file mode 100644
index 0000000..39abc01
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/Dockerfile
@@ -0,0 +1,16 @@
+FROM websphere-liberty:latest
+
+ENV SERVERDIRNAME reviews
+
+ADD ./servers/LibertyProjectServer /opt/ibm/wlp/usr/servers/defaultServer/
+
+RUN /opt/ibm/wlp/bin/installUtility install  --acceptLicense /opt/ibm/wlp/usr/servers/defaultServer/server.xml
+
+ARG service_version
+ARG enable_ratings
+ARG star_color
+ENV SERVICE_VERSION ${service_version:-v1}
+ENV ENABLE_RATINGS ${enable_ratings:-false}
+ENV STAR_COLOR ${star_color:-black}
+
+CMD /opt/ibm/wlp/bin/server run defaultServer
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/build.gradle b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/build.gradle
new file mode 100644
index 0000000..5cc57f2
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'eclipse'
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+}
+
+task copyApplication(type: Copy) {
+    from '../reviews-application/build/libs/reviews-application-1.0.war'
+    into 'servers/LibertyProjectServer/apps/'
+}
+
+task build(dependsOn: ['copyApplication']){
+}
+
+task clean {
+    delete "servers/LibertyProjectServer/apps"
+    delete "servers/LibertyProjectServer/lib"
+    delete "servers/LibertyProjectServer/logs"
+    delete "servers/LibertyProjectServer/workarea"
+    delete "servers/LibertyProjectServer/resources"
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/servers/LibertyProjectServer/server.xml b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/servers/LibertyProjectServer/server.xml
new file mode 100644
index 0000000..aac20bd
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/servers/LibertyProjectServer/server.xml
@@ -0,0 +1,33 @@
+<!-- Copyright (c) 2017 Istio Authors
+
+  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.-->
+<server description="Sample Liberty server">
+
+    <featureManager>
+        <feature>jaxrs-2.0</feature>
+        <feature>jsonp-1.0</feature>
+    </featureManager>
+
+    <httpEndpoint host="*" httpPort="9080" httpsPort="-1"
+                  id="defaultHttpEndpoint"/>
+
+    <webContainer deferServletLoad="false"/>
+    <applicationMonitor dropinsEnabled="false" updateTrigger="mbean"/>
+    <config updateTrigger="mbean"/>
+
+
+    <executor coreThreads="5"/>
+
+    <webApplication contextRoot="/" id="reviews-app" location="reviews-application-1.0.war" name="reviews-app"/>
+
+</server>
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/shared/.gitkeep b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/shared/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/shared/.gitkeep
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/EndpointTest.java b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/EndpointTest.java
new file mode 100644
index 0000000..d594d53
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/EndpointTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Istio Authors
+ *
+ * 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 it;
+
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+public class EndpointTest {
+
+    public void testEndpoint(String endpoint, String expectedOutput) {
+        String port = System.getProperty("liberty.test.port");
+        String war = System.getProperty("war.name");
+        String url = "http://localhost:" + port + "/" + war + endpoint;
+        System.out.println("Testing " + url);
+        Response response = sendRequest(url, "GET");
+        int responseCode = response.getStatus();
+        assertTrue("Incorrect response code: " + responseCode,
+                   responseCode == 200);
+        
+        String responseString = response.readEntity(String.class);
+        response.close();
+        assertTrue("Incorrect response, response is " + responseString, responseString.contains(expectedOutput));
+    }
+
+    public Response sendRequest(String url, String requestType) {
+        Client client = ClientBuilder.newClient();
+        System.out.println("Testing " + url);
+        WebTarget target = client.target(url);
+        Invocation.Builder invoBuild = target.request();
+        Response response = invoBuild.build(requestType).invoke();
+        return response;
+    }
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/TestApplication.java b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/TestApplication.java
new file mode 100644
index 0000000..de7fa29
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/TestApplication.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Istio Authors
+ *
+ * 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 it;
+
+import org.junit.Test;
+
+public class TestApplication extends EndpointTest {
+
+    @Test
+    public void testDeployment() {
+        testEndpoint("/index.html", "<h1>Welcome to your Liberty Application</h1>");
+    }
+
+}
diff --git a/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/rest/LibertyRestEndpointTest.java b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/rest/LibertyRestEndpointTest.java
new file mode 100644
index 0000000..916841f
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/reviews-wlpcfg/src/test/java/it/rest/LibertyRestEndpointTest.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright (c) 2017 Istio Authors
+ *
+ * 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 it.rest;
+
+import it.EndpointTest;
+
+import org.junit.Test;
+
+public class LibertyRestEndpointTest extends EndpointTest {
+
+    @Test
+    public void testDeployment() {
+        testEndpoint("/rest", "Hello from the REST endpoint!");
+    }
+}
diff --git a/demo/apps/bookinfo/src/reviews/settings.gradle b/demo/apps/bookinfo/src/reviews/settings.gradle
new file mode 100644
index 0000000..019d1e8
--- /dev/null
+++ b/demo/apps/bookinfo/src/reviews/settings.gradle
@@ -0,0 +1,4 @@
+rootProject.name = 'reviews'
+
+include 'reviews-application'
+include 'reviews-wlpcfg'
diff --git a/demo/apps/httpbin/httpbin.yml b/demo/apps/httpbin/httpbin.yml
new file mode 100644
index 0000000..9b93434
--- /dev/null
+++ b/demo/apps/httpbin/httpbin.yml
@@ -0,0 +1,70 @@
+# Copyright 2017 Istio Authors
+#
+#   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.
+
+##################################################################################################
+# httpbin service
+##################################################################################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: httpbin
+  labels:
+    app: httpbin
+spec:
+  ports:
+  - name: http
+    port: 8000
+  selector:
+    app: httpbin
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: httpbin
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        app: httpbin
+    spec:
+      containers:
+      - image: docker.io/citizenstig/httpbin
+        imagePullPolicy: IfNotPresent
+        name: httpbin
+        ports:
+        - containerPort: 8000
+---
+apiVersion: extensions/v1beta1
+kind: Ingress
+metadata:
+  name: httbin-gateway
+  annotations:
+    kubernetes.io/ingress.class: "istio"
+spec:
+  rules:
+  - http:
+      paths:
+      - path: /get
+        backend:
+          serviceName: httpbin
+          servicePort: 8000
+      - path: /status
+        backend:
+          serviceName: httpbin
+          servicePort: 8000
+      - path: /delay
+        backend:
+          serviceName: httpbin
+          servicePort: 8000
diff --git a/images/istio.png b/images/istio.png
new file mode 100644
index 0000000..632762f
--- /dev/null
+++ b/images/istio.png
Binary files differ
diff --git a/istio.VERSION b/istio.VERSION
new file mode 100644
index 0000000..3220a7c
--- /dev/null
+++ b/istio.VERSION
@@ -0,0 +1,9 @@
+# DO NOT EDIT THIS FILE MANUALLY instead use
+# tests/updateVersion.sh (see tests/README.md)
+export CA_HUB="docker.io/istio"
+export CA_TAG="0.1.3"
+export MIXER_HUB="docker.io/istio"
+export MIXER_TAG="0.1.3"
+export ISTIOCTL_URL="https://storage.googleapis.com/istio-artifacts/manager/stable-c5aab85/artifacts/istioctl"
+export MANAGER_HUB="docker.io/istio"
+export MANAGER_TAG="0.1.4-c5aab85"
diff --git a/setup/istio.yaml b/setup/istio.yaml
new file mode 100644
index 0000000..173644a
--- /dev/null
+++ b/setup/istio.yaml
@@ -0,0 +1,222 @@
+# GENERATED FILE. Use with Kubernetes 1.5+
+# TO UPDATE, modify files in install/kubernetes/templates and run updateVersion.sh
+# Mixer
+apiVersion: v1
+kind: Service
+metadata:
+  name: istio-mixer
+  labels:
+    istio: mixer
+spec:
+  ports:
+  - name: tcp
+    port: 9091
+  - name: configapi
+    port: 9094
+  - name: prometheus
+    port: 42422
+  selector:
+    istio: mixer
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: istio-mixer
+spec:
+  replicas: 1
+  template:
+    metadata:
+      annotations:
+        alpha.istio.io/sidecar: ignore
+      labels:
+        istio: mixer
+    spec:
+      containers:
+      - name: mixer
+#        image: docker.io/istio/mixer:0.1.2-6bfa390
+        image: kidiyoor/mixer:apigeev3
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 9091
+        - containerPort: 9094
+        - containerPort: 42422
+        args:
+          - --configStoreURL=fs:///etc/opt/mixer/configroot
+          - --logtostderr
+          - -v
+          - "3"
+        volumeMounts:
+        - name: apigee-mixer-adapter
+          mountPath: /etc/opt/mixer/apigee
+          readOnly: true
+      volumes:
+      - name: apigee-mixer-adapter
+        secret:
+          secretName: apigee-mixer-adapter
+---
+# Manager service for discovery
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: istio
+data:
+  mesh: |-
+    # Uncomment the following line to enable mutual TLS between proxies
+    # authPolicy: MUTUAL_TLS
+    mixerAddress: istio-mixer:9091
+    discoveryAddress: istio-manager:8080
+    ingressService: istio-ingress
+    zipkinAddress: zipkin:9411
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: istio-manager
+  labels:
+    istio: manager
+spec:
+  ports:
+  - port: 8080
+    name: http-discovery
+  - port: 8081
+    name: http-apiserver
+  selector:
+    istio: manager
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: istio-manager-service-account
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: istio-manager
+spec:
+  replicas: 1
+  template:
+    metadata:
+      annotations:
+        alpha.istio.io/sidecar: ignore
+      labels:
+        istio: manager
+    spec:
+      serviceAccountName: istio-manager-service-account
+      containers:
+      - name: discovery
+        image: docker.io/istio/manager:0.1.2-6dbd19d
+        imagePullPolicy: Always
+        args: ["discovery", "-v", "2"]
+        ports:
+        - containerPort: 8080
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+      - name: apiserver
+        image: docker.io/istio/manager:0.1.2-6dbd19d
+        imagePullPolicy: Always
+        args: ["apiserver", "-v", "2"]
+        ports:
+        - containerPort: 8081
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+---
+################################
+# Istio ingress controller
+################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: istio-ingress
+  labels:
+    istio: ingress
+spec:
+  type: LoadBalancer
+  ports:
+  - port: 80
+#   nodePort: 32000
+    name: http
+  - port: 443
+    name: https
+  selector:
+    istio: ingress
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: istio-ingress-service-account
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: istio-ingress
+spec:
+  replicas: 1
+  template:
+    metadata:
+      annotations:
+        alpha.istio.io/sidecar: ignore
+      labels:
+        istio: ingress
+    spec:
+      serviceAccountName: istio-ingress-service-account
+      containers:
+      - name: istio-ingress
+        image: docker.io/istio/proxy_debug:0.1.2-6dbd19d
+        args: ["proxy", "ingress", "-v", "2"]
+        imagePullPolicy: Always
+        ports:
+        - containerPort: 80
+        - containerPort: 443
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+---
+
+################################
+# Istio egress envoy
+################################
+apiVersion: v1
+kind: Service
+metadata:
+  name: istio-egress
+spec:
+  ports:
+  - port: 80
+  selector:
+    istio: egress
+---
+apiVersion: extensions/v1beta1
+kind: Deployment
+metadata:
+  name: istio-egress
+spec:
+  replicas: 1
+  template:
+    metadata:
+      labels:
+        istio: egress
+    spec:
+      containers:
+      - name: proxy
+        image: docker.io/istio/proxy_debug:0.1.2-6dbd19d
+        imagePullPolicy: Always
+        args: ["proxy", "egress", "-v", "2"]
+        env:
+        - name: POD_NAMESPACE
+          valueFrom:
+            fieldRef:
+              apiVersion: v1
+              fieldPath: metadata.namespace
+---
+