CORD-3105 Document SimpleExampleService

Change-Id: I68a04a90c967260f069ffb7d9499908659a95199
diff --git a/docs/simple-example-service.md b/docs/simple-example-service.md
new file mode 100644
index 0000000..22e815b
--- /dev/null
+++ b/docs/simple-example-service.md
@@ -0,0 +1,142 @@
+# SimpleExampleService #
+
+`SimpleExampleService` is a service intended to demonstrate integration with the XOS `kubernetes` service. `SimpleExampleService` provides a `SimpleExampleServiceInstance` model that generates and hosts a web page, displaying two text strings on the web page: a `service_message` and a `tenant_message`. Each time a `SimpleExampleServiceInstance` is created, a corresponding `KubernetesServiceInstance` will also be created which will in turn cause a Kubernetes pod to be created that runs an apache web server hosting the web page.
+
+Destroying the `SimpleExampleServiceInstance` will cause the linked `KubernetesServiceInstance` to also be destroyed, which will in turn cause the Kubernetes pod to be cleaned up.
+
+## Limitations ##
+
+This service does not yet demonstrate dataplane connectivity to subscribers.
+
+## Implementation ##
+
+Inside the `SimpleExampleService` repository's `xos/synchronizer` directory, there are three key parts to the service.
+
+1. The `models` directory. This directory contains the models that comprise `SimpleExampleService`. The full text of the models are specified in a file, `simpleexampleservice.xproto`. A summary of the models is below:
+
+    * `SimpleExampleService` holds global service-wide settings, including a `service_message`, which appears in all web pages generated by `SimpleExampleService`, and a `service_secret` that is installed into all container that run the web servers.
+
+    * `ServiceInstanceWithCompute` is an intermediate model, inheriting from `ServiceInstance` and being inherited by `SimpleExampleServiceInstance`. It augments `ServiceInstance` with a `compute_instance` field that may be used to link a `ServiceInstance` to the `ComputeServiceInstance` that will hold the compute resources necessary to implement the `ServiceInstance`. This model will likely be migrated to the XOS core at some point as more containerized VNFs are developed. For those familiar with `CORD-5.0`, `ServiceInstanceWithCompute` serves the same purpose that `TenantWithContainer` and/or `ServiceInstanceWithContainer` served in `CORD-5.0` and prior releases.
+
+    * `SimpleExampleServiceInstance` holds per-tenant settings, including a `tenant_message`. Each `SimpleExampleServiceInstance` corresponds to one web server serving one web page. This model has relations for `foreground_color` and `background_color` that allow some additional customization of the served page. `tenant_secret` is a secret that is installed into the container running the web server
+
+    * `ColorNew` implements the color model used by the `foreground_color` and `background_color` fields of `SimpleExampleServiceInstance`.
+
+    * `EmbeddedImageNew` allows embedded images to be attached to web pages. As the foreign key relation is from the embedded image to the service instance, this forms a many-to-one relation that allows many images to be attached to a single web page. 
+
+2. The `model_policies` directory contains a model policy. This model_policy executes code every time a `SimpleExampleServiceInstance` is created, updated, or deleted. 
+
+    Rather than reproducing the full text of the code here, the actions are summarized below, and it's suggested the reader consult `model_policy_simpleexampleserviceinstance.py` for reference. 
+
+    When a new `SimpleExampleServiceInstance` is created, the model policy creates a `KubernetesConfigMap`, `KubernetesSecret`, `KubernetesServiceInstance` and the necessary objects to mount the configmap and secret into the service instance. The `SimpleExampleServiceInstance` is updated with a relation to the `KubernetesServiceInstance`, to make it easy to handle updates and deletions later. 
+
+    When a `SimpleExampleServiceInstance` is updated, the config map is modified to contain the new data, and the related `KubernetesServiceInstance` is resaved, to cause it to be resynchronized by the Kubernetes synchronizer.
+
+    When a `SimpleExampleServiceInstance` is deleted, the related `KubernetesServiceInstance` is deleted.
+
+3. The `event_steps` directory contains an event step. This event step listens for Kafka events on the Kafka topic `SimpleExampleEvent`. It assumes each event is a json-encoded dictionary containing a `service_instance_name` and `tenant_message`. The `SimpleExampleServiceInstance` is looked up by name, the `tenant_message` is updated, and the object is re-saved. Saving the object will then trigger the update model policy to run. 
+
+## Demonstration ##
+
+The following subsections work through a quick demonstration of `SimpleExampleService`. 
+
+### Prerequisites ###
+
+This document assumes that you have already installed Kubernetes in your development environment.
+
+### Deploy the necessary profiles ###
+
+It's necessary for us to deploy three helm charts, `xos-core`, `base-kubernetes`. and `demo-simpleexampleservice`. 
+
+> Note: If you've already installed a different set of XOS profile helm charts, such as the `rcord-lite` profile, then you may wish to uninstall those, as there's no guarantee that the `base-kubernetes` and `demo-simpleexampleservice` helm-charts can be layered on top of an existing XOS profile.
+
+If you have not already done so, execute the following commands:
+
+```bash
+# Go into the helm-charts repository
+cd ~/cord/helm-charts
+
+# Initialize helm
+helm init
+
+# Install the xos-core helm chart
+helm dep update xos-core
+helm install xos-core -n xos-core
+
+# Install the base-kubernetes helm chart
+helm dep update xos-profiles/base-kubernetes
+helm install xos-profiles/base-kubernetes -n base-kubernetes
+
+# Install the demo-simpleexampleservice helm chart
+helm dep update xos-profiles/demo-simpleexampleservice
+helm install xos-profiles/demo-simpleexampleservice -n demo-simpleexampleservice
+```
+
+The helm charts above install successive layers of CORD. The first chart, `xos-core` installs core components such as the XOS core, database, TOSCA engine, etc. The second chart, `base-kubernetes` installs the XOS Kubernetes Service, which provides modeling and synchronizers for instantiating Kubernetes resources using the XOS data model. The final helm chart, `demo-simpleexampleservice` installs the synchronizer for `SimpleExampleService`, including registering models with the core.
+
+> Note: It will take some time for the various helm charts to deploy and the containers to come online. We recommend using `kubectl get pods` to explore the state of the system during deployment. In particular, note the presence of `tosca-loader` containers. These containers are responsible for running TOSCA that configures services in the stack. The `tosca-loaders` may error and retry several times as they wait for services to be dynamically loaded. This is normal, and eventually the `tosca-loader` containers will enter the `Completed` state.
+
+Use `kubectl get pods` to verify that all containers in the profile are successful and none are in error state. At this point, we've installed all of the necessary infrastructure to support `SimpleExampleService`, such as registering its models and starting its synchronizer, but we haven't actually provisioned a `SimpleExampleServiceInstance` yet. We will do that step next.  
+
+### Create a `SimpleExampleServiceInstance` using TOSCA ###
+
+This step will provision a `SimpleExampleServiceInstance`. This ServiceInstance will be responsible for generating a web page and hosting that we page using resoucres of the Kubernetes service. `SimpleExampleService` is a multi-tenant service, and the operator may provision man `SimpleExampleServiceInstance`, each one an individual tenant of the service and each one generating a custom web page. This demo will take you through creating one ServiceInstance, and leave creating further ServiceInstances as an exercise.
+
+We will demonstrate using TOSCA to create the `SimpleExampleServiceInstance`, but this is not the only mechanism available. The steps here could alternatively be done in the XOS GUI, by using the XOS REST or gRPC APIs, or implemented as part of the model policies of some other service. 
+
+* Set your username and password.
+
+```bash
+USERNAME=admin@opencord.org
+PASSWORD=letmein
+```
+
+* Run the TOSCA recipe to create a `SimpleExampleServiceInstance`.
+
+```bash
+TOSCA_URL=$(minikube service xos-tosca --url)
+TOSCA_FN=~/cord/orchestration/xos_services/simpleexampleservice/xos/examples/SimpleExampleServiceInstance.yaml
+curl -H "xos-username: $USERNAME" -H "xos-password: $PASSWORD" -X POST --data-binary @$TOSCA_FN $TOSCA_URL/run
+```
+
+* Please wait a few seconds for model policies to run, the Kubernetes synchronizer to instantiate containers, etc.
+
+* View the status. 
+
+```bash
+CHAMELEON_URL=$(minikube service xos-chameleon --url)
+python ~/cord/orchestration/xos_services/simpleexampleservice/xos/examples//show-instances.py $CHAMELEON_URL $USERNAME $PASSWORD
+```
+
+> Note: You may have to re-execute the above a few times while waiting for the objects to be created. If all is successful, eventually you will see an IP address assigned to the service instance. 
+
+* View the web page
+
+    Enter one of the other Kubernetes containers (`kubectl get pods` can be used to retrieve a list), any container such as one of the synchronizer containers will do, and perform a curl on the IP address obtained in the previous step.
+
+### Use the Event Bus to modify a SimpleExampleServiceInstace ###
+
+The event bus is an optional mechanism that may be used to interact with services. `SimpleExampleService` implements a single `EventStep`, which listens on a Kafka topic and allows the `tenant_message` to be updated.  
+
+* Ensure Kafka is running
+
+```bash
+helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
+helm install --name cord-kafka --set replicas=1 incubator/kafka
+```
+
+* Send an Event to update a web page
+
+    Enter one of the other Kubernetes containers, install the kafka library (`pip install kafka`) and execute the follow python:
+
+```python
+import json
+from kafka import KafkaProducer
+producer = KafkaProducer(bootstrap_servers="cord-kafka-kafka")
+producer.send("SimpleExampleEvent", json.dumps({"service_instance": "My Simple Example Service Instance", "tenant_message": "Earth"}))
+producer.flush()
+```
+
+* View the web page
+
+    Enter one of the other Kubernetes containers, any container such as one of the synchronizer containers will do, and perform a curl on the same IP address obtained in previous section. It may take up to a few minutes for the container to be updated with the new state from the event.
diff --git a/xos/examples/README.md b/xos/examples/README.md
deleted file mode 100644
index 5008de7..0000000
--- a/xos/examples/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-The following describes a demo that brings up a `SimpleExampleServiceInstance`. The purpose of this ServiceInstance is to host a single web inside of a Kubernetes container. Creating the `SimpleExampleServiceInstance` will cause a model policy to be invoked, which will create the necessary Kubernetes resources to launch the web server and configure it to host the desired page.
-
-1. Set your username and password
-
-```
-USERNAME=admin@opencord.org
-PASSWORD=letmein
-```
-
-2. Run the TOSCA recipe
-
-```
-TOSCA_URL=$(minikube service xos-tosca --url)
-curl -H "xos-username: $USERNAME" -H "xos-password: $PASSWORD" -X POST --data-binary @SimpleExampleServiceInstance.yaml $TOSCA_URL/run
-```
-
-3. Wait a few seconds for the Kubernetes instances to be created.
-
-4. View the status
-
-```
-CHAMELEON_URL=$(minikube service xos-chameleon --url)
-python ./show-instances.py $CHAMELEON_URL $USERNAME $PASSWORD
-```
-
-5. View the web page
-Enter one of the other Kubernetes containers, any container such as one of the synchronizer containers will do, and perform a curl on the IP address obtained in step 4.
-
-6. Ensure Kafka is running
-
-```
-helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator
-helm install --name cord-kafka --set replicas=1 incubator/kafka
-```
-
-7. Send an Event to update a web page
-Enter one of the other Kubernetes containers, install the kafka library (`pip install kafka`) and execute the follow python:
-
-```
-import json
-from kafka import KafkaProducer
-producer = KafkaProducer(bootstrap_servers="cord-kafka-kafka")
-producer.send("SimpleExampleEvent", json.dumps({"service_instance": "My Simple Example Service Instance", "tenant_message": "Earth"}))
-producer.flush()
-```