From 9e328761338a2057a1ee57b4051db21f08818fe3 Mon Sep 17 00:00:00 2001 From: Ben Parees Date: Mon, 15 Sep 2014 17:30:09 -0400 Subject: [PATCH] initial create of example of application creation flow --- examples/simple-ruby-app/QUICKSTART.md | 31 ++++ examples/simple-ruby-app/README.md | 123 ++++++++++++++++ .../simple-ruby-app/buildcfg/buildcfg.json | 11 ++ .../buildinvoke/pingevent.json | 27 ++++ .../buildinvoke/pushevent.json | 136 ++++++++++++++++++ examples/simple-ruby-app/cleanup.sh | 9 ++ examples/simple-ruby-app/logs/.gitkeep | 0 examples/simple-ruby-app/processed/.gitkeep | 0 examples/simple-ruby-app/run.sh | 113 +++++++++++++++ .../simple-ruby-app/template/template.json | 74 ++++++++++ 10 files changed, 524 insertions(+) create mode 100644 examples/simple-ruby-app/QUICKSTART.md create mode 100644 examples/simple-ruby-app/README.md create mode 100644 examples/simple-ruby-app/buildcfg/buildcfg.json create mode 100644 examples/simple-ruby-app/buildinvoke/pingevent.json create mode 100644 examples/simple-ruby-app/buildinvoke/pushevent.json create mode 100755 examples/simple-ruby-app/cleanup.sh create mode 100644 examples/simple-ruby-app/logs/.gitkeep create mode 100644 examples/simple-ruby-app/processed/.gitkeep create mode 100755 examples/simple-ruby-app/run.sh create mode 100644 examples/simple-ruby-app/template/template.json diff --git a/examples/simple-ruby-app/QUICKSTART.md b/examples/simple-ruby-app/QUICKSTART.md new file mode 100644 index 000000000000..2b95a546e611 --- /dev/null +++ b/examples/simple-ruby-app/QUICKSTART.md @@ -0,0 +1,31 @@ +Quickstart +---------- +This section describes how you can quickly test that your OpenShift environment is setup properly, and allows you to watch the flow of an application creation and build in OpenShift. + +To do this, run: + + $ ./run.sh + +This will: + +1. Launch the openshift server + * Logs are available in logs/openshift.log + +2. Submit the template/template.json for parameterization + +3. Store the resulting config json in processed/template.processed.json + +4. Submit the config json to openshift for creation + +5. Confirm the application is created/accessible via curl + +6. Trigger a new build of the application + +7. Show the new docker image created on your local system as a result of the build + * Normally the next step would be to push this image to a docker registry and then create a new openshift application based on it, or cause the existing application to be redeployed with the new image. See the next section to exercise this flow. + +To reset your system after running this example, you can run: + + $ ./cleanup.sh + +This will stop the openshift process, remove the etcd storage, and kill all docker containers running on your host system. (**Use with caution!** Docker containers unrelated to openshift will also be killed by this script) diff --git a/examples/simple-ruby-app/README.md b/examples/simple-ruby-app/README.md new file mode 100644 index 000000000000..5332fc2a19cb --- /dev/null +++ b/examples/simple-ruby-app/README.md @@ -0,0 +1,123 @@ +OpenShift 3 Application Lifecycle Sample +======================================== + +This is a set of configuration files and scripts which work with Openshift 3 to create a new application and perform application builds. + +This example assumes you have successfully built the `openshift` binary executable and have Docker installed/working. See https://github.com/openshift/origin/blob/master/CONTRIBUTING.adoc. + + +Application Build, Deploy, and Update Flow +-------------- +This section covers how to perform all the steps of building, deploying, and updating an application on the OpenShift platform. + +Note: If you just want to quickly validate your environment or see the expected results, you can run the quickstart script described [here](QUICKSTART.md) + +All commands assume the `openshift` binary is in your path: + +1. Launch `openshift` + + $ USE_HOST_DOCKER_SOCKET=true openshift start --listenAddr="0.0.0.0:8080" + +2. Fork the [ruby sample repository](https://github.com/openshift/ruby-hello-world) + +3. *Optional:* Add the following webhook to your new github repository: + + $ http://:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github + * Note: Using the webhook requires your OpenShift server be publicly accessible so github can reach it to invoke the hook. + +4. Edit buildcfg/buildcfg.json + * Update the sourceURI to point to your forked repository. + * Update the imageTag to reflect your Docker username (eg <docker_username>/origin-ruby-sample) + * The built image will be tagged with this id and pushed to Docker later. + +5. Create a build configuration for your application. This configuration is used by OpenShift to rebuild your application's Docker image (eg when you push changes to the application source). + + $ openshift kube create buildConfigs -c buildcfg/buildcfg.json + + You should see the build configuration returned as output (SourceURI will depend on your repo name): + + ID Type SourceURI + ---------- ---------- ---------- + build100 docker git://github.com/openshift/ruby-hello-world.git + +6. Trigger an initial build of your application + * If you setup the github webhook in step 3, push a change to app.rb in your ruby sample repository from step 2. + * Otherwise you can simulate the webhook invocation by running: + + $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @buildinvoke/pushevent.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github + + In the OpenShift logs (logs/openshift.log) you should see something like: + + I0916 13:50:22.479529 21375 log.go:134] POST /osapi/v1beta1/buildConfigHooks/build100/secret101/github + + which confirms the webhook was triggered. + +7. Monitor the builds and wait for the status to go to "complete" + + $ openshift kube list builds + + Sample output: + + ID Status Pod ID + ---------- ---------- ---------- + 20f54507-3dcd-11e4-984b-3c970e3bf0b7 complete build-docker-20f54507-3dcd-11e4-984b-3c970e3bf0b7 + + +8. Once the build is complete, push the resulting image to your Docker repository: + + $ docker push /origin-ruby-sample + +9. Modify the application template to reference your Docker image: + * Change "image": "openshift/origin-ruby-sample", to "image": "<docker_username>/origin-ruby-sample", + +10. Submit the application template for processing: + + $ curl -sld @template/template.json http://localhost:8080/osapi/v1beta1/templateConfigs > processed/template.processed.json + +11. Create the application using the processed template: + + $ openshift kube apply -c processed/template.processed.json + +12. Wait for the application's frontend pod to be started: + + $ openshift kube list pods + + Sample output: + + ID Image(s) Host Labels Status + ---------- ---------- ---------- ---------- ---------- + fc66bffd-3dcc-11e4-984b-3c970e3bf0b7 openshift/origin-ruby-sample 127.0.0.1/ name=frontend,replicationController=frontendController Running + +13. Confirm the application is now accessible via the frontend service on port 5432: + + $ curl localhost:5432 + + Sample output: + + Hello World! + User is adminM5K + Password is qgRpLNGO + DB password is dQfUlnTG + + +14. Make an additional change to your ruby sample app.rb file and push it. + * If you do not have the webhook enabled, you'll have to manually trigger another build: + + $ curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @buildinvoke/pushevent.json http://localhost:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github + +15. Repeat steps 7-8 + +16. Locate the container running the ruby application and kill it: + + $ docker kill `docker ps | grep origin-ruby-sample | awk '{print $1}'` + +17. Use 'docker ps' to watch as OpenShift automatically recreates the killed container using the latest version of your image. Once the container is recreated, curl the application to see the change you made in step 14. + + $ curl localhost:5432 + +Congratulations, you've successfully deployed and updated an application on OpenShift. To clean up your environment, you can run: + + $ ./cleanup.sh + + This will stop the `openshift` process, remove the etcd storage, and kill all Docker containers running on your host system. (**Use with caution!** Docker containers unrelated to OpenShift will also be killed by this script) + \ No newline at end of file diff --git a/examples/simple-ruby-app/buildcfg/buildcfg.json b/examples/simple-ruby-app/buildcfg/buildcfg.json new file mode 100644 index 000000000000..96c9be55e6d9 --- /dev/null +++ b/examples/simple-ruby-app/buildcfg/buildcfg.json @@ -0,0 +1,11 @@ +{ + "id": "build100", + "desiredInput": + { + "type": "docker", + "sourceURI": "git://github.com/openshift/ruby-hello-world.git", + "imageTag": "openshift/origin-ruby-sample", + }, + + "secret": "secret101" +} diff --git a/examples/simple-ruby-app/buildinvoke/pingevent.json b/examples/simple-ruby-app/buildinvoke/pingevent.json new file mode 100644 index 000000000000..81eb2a5150a3 --- /dev/null +++ b/examples/simple-ruby-app/buildinvoke/pingevent.json @@ -0,0 +1,27 @@ +{ + "zen":"Encourage flow.", + "hook":{ + "url":"https://api.github.com/repos/anonUser/anonRepo/hooks/2896466", + "test_url":"https://api.github.com/repos/anonUser/anonRepo/hooks/2896466/test", + "id":2896466, + "name":"web", + "active":true, + "events":[ + "push" + ], + "config":{ + "secret":"", + "url":"http://example.com/gitwebhook", + "content_type":"json", + "insecure_ssl":"0" + }, + "last_response":{ + "code":null, + "status":"unused", + "message":null + }, + "updated_at":"2014-08-28T14:25:47Z", + "created_at":"2014-08-28T14:25:47Z" + }, + "hook_id":2896466 +} diff --git a/examples/simple-ruby-app/buildinvoke/pushevent.json b/examples/simple-ruby-app/buildinvoke/pushevent.json new file mode 100644 index 000000000000..e087faa276b4 --- /dev/null +++ b/examples/simple-ruby-app/buildinvoke/pushevent.json @@ -0,0 +1,136 @@ +{ + "ref":"refs/heads/master", + "after":"9bdc3a26ff933b32f3e558636b58aea86a69f051", + "before":"0000000000000000000000000000000000000000", + "created":true, + "deleted":false, + "forced":true, + "compare":"https://github.com/anonUser/anonRepo/commit/9bdc3a26ff93", + "commits":[ + { + "id":"9bdc3a26ff933b32f3e558636b58aea86a69f051", + "distinct":true, + "message":"Added license", + "timestamp":"2014-08-28T16:55:36+02:00", + "url":"https://github.com/anonUser/anonRepo/commit/9bdc3a26ff933b32f3e558636b58aea86a69f051", + "author":{ + "name":"Anonymous User", + "email":"anonUser@example.com" + }, + "committer":{ + "name":"Anonymous User", + "email":"anonUser@example.com" + }, + "added":[ + "LICENSE" + ], + "removed":[ + + ], + "modified":[ + + ] + } + ], + "head_commit":{ + "id":"9bdc3a26ff933b32f3e558636b58aea86a69f051", + "distinct":true, + "message":"Added license", + "timestamp":"2014-08-28T16:55:36+02:00", + "url":"https://github.com/anonUser/anonRepo/commit/9bdc3a26ff933b32f3e558636b58aea86a69f051", + "author":{ + "name":"Anonymous User", + "email":"anonUser@example.com" + }, + "committer":{ + "name":"Anonymous User", + "email":"anonUser@example.com" + }, + "added":[ + "LICENSE" + ], + "removed":[ + + ], + "modified":[ + + ] + }, + "repository":{ + "id":23354788, + "name":"anonRepo", + "full_name":"anonUser/anonRepo", + "owner":{ + "name":"anonUser", + "email":"anonUser@example.com" + }, + "private":false, + "html_url":"https://github.com/anonUser/anonRepo", + "description":"Git webhook implementation in Go.", + "fork":false, + "url":"https://github.com/anonUser/anonRepo", + "forks_url":"https://api.github.com/repos/anonUser/anonRepo/forks", + "keys_url":"https://api.github.com/repos/anonUser/anonRepo/keys{/key_id}", + "collaborators_url":"https://api.github.com/repos/anonUser/anonRepo/collaborators{/collaborator}", + "teams_url":"https://api.github.com/repos/anonUser/anonRepo/teams", + "hooks_url":"https://api.github.com/repos/anonUser/anonRepo/hooks", + "issue_events_url":"https://api.github.com/repos/anonUser/anonRepo/issues/events{/number}", + "events_url":"https://api.github.com/repos/anonUser/anonRepo/events", + "assignees_url":"https://api.github.com/repos/anonUser/anonRepo/assignees{/user}", + "branches_url":"https://api.github.com/repos/anonUser/anonRepo/branches{/branch}", + "tags_url":"https://api.github.com/repos/anonUser/anonRepo/tags", + "blobs_url":"https://api.github.com/repos/anonUser/anonRepo/git/blobs{/sha}", + "git_tags_url":"https://api.github.com/repos/anonUser/anonRepo/git/tags{/sha}", + "git_refs_url":"https://api.github.com/repos/anonUser/anonRepo/git/refs{/sha}", + "trees_url":"https://api.github.com/repos/anonUser/anonRepo/git/trees{/sha}", + "statuses_url":"https://api.github.com/repos/anonUser/anonRepo/statuses/{sha}", + "languages_url":"https://api.github.com/repos/anonUser/anonRepo/languages", + "stargazers_url":"https://api.github.com/repos/anonUser/anonRepo/stargazers", + "contributors_url":"https://api.github.com/repos/anonUser/anonRepo/contributors", + "subscribers_url":"https://api.github.com/repos/anonUser/anonRepo/subscribers", + "subscription_url":"https://api.github.com/repos/anonUser/anonRepo/subscription", + "commits_url":"https://api.github.com/repos/anonUser/anonRepo/commits{/sha}", + "git_commits_url":"https://api.github.com/repos/anonUser/anonRepo/git/commits{/sha}", + "comments_url":"https://api.github.com/repos/anonUser/anonRepo/comments{/number}", + "issue_comment_url":"https://api.github.com/repos/anonUser/anonRepo/issues/comments/{number}", + "contents_url":"https://api.github.com/repos/anonUser/anonRepo/contents/{+path}", + "compare_url":"https://api.github.com/repos/anonUser/anonRepo/compare/{base}...{head}", + "merges_url":"https://api.github.com/repos/anonUser/anonRepo/merges", + "archive_url":"https://api.github.com/repos/anonUser/anonRepo/{archive_format}{/ref}", + "downloads_url":"https://api.github.com/repos/anonUser/anonRepo/downloads", + "issues_url":"https://api.github.com/repos/anonUser/anonRepo/issues{/number}", + "pulls_url":"https://api.github.com/repos/anonUser/anonRepo/pulls{/number}", + "milestones_url":"https://api.github.com/repos/anonUser/anonRepo/milestones{/number}", + "notifications_url":"https://api.github.com/repos/anonUser/anonRepo/notifications{?since,all,participating}", + "labels_url":"https://api.github.com/repos/anonUser/anonRepo/labels{/name}", + "releases_url":"https://api.github.com/repos/anonUser/anonRepo/releases{/id}", + "created_at":1409063699, + "updated_at":"2014-08-26T14:34:59Z", + "pushed_at":1409238007, + "git_url":"git://github.com/anonUser/anonRepo.git", + "ssh_url":"git@github.com:anonUser/anonRepo.git", + "clone_url":"https://github.com/anonUser/anonRepo.git", + "svn_url":"https://github.com/anonUser/anonRepo", + "homepage":null, + "size":0, + "stargazers_count":0, + "watchers_count":0, + "language":null, + "has_issues":true, + "has_downloads":true, + "has_wiki":true, + "forks_count":0, + "mirror_url":null, + "open_issues_count":0, + "forks":0, + "open_issues":0, + "watchers":0, + "default_branch":"master", + "stargazers":0, + "master_branch":"master" + }, + "pusher":{ + "name":"anonUser", + "email":"anonUser@example.com" + } +} diff --git a/examples/simple-ruby-app/cleanup.sh b/examples/simple-ruby-app/cleanup.sh new file mode 100755 index 000000000000..10add18fedd5 --- /dev/null +++ b/examples/simple-ruby-app/cleanup.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +echo "Killing openshift all-in-one server" +killall openshift +echo "Cleaning up openshift etcd content" +rm -rf openshift.local.etcd +echo "Killing all docker containers on host" +docker kill `docker ps --no-trunc -q` + diff --git a/examples/simple-ruby-app/logs/.gitkeep b/examples/simple-ruby-app/logs/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/simple-ruby-app/processed/.gitkeep b/examples/simple-ruby-app/processed/.gitkeep new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/examples/simple-ruby-app/run.sh b/examples/simple-ruby-app/run.sh new file mode 100755 index 000000000000..8679232e78f2 --- /dev/null +++ b/examples/simple-ruby-app/run.sh @@ -0,0 +1,113 @@ +#!/bin/bash + +# openshift server host +OSHOST=localhost + +# openshift binary +openshift="../../_output/go/bin/openshift" + +# OPTIONAL: Wipe out previous openshift/k8s deployment information for a +# clean start. +rm -rf openshift.local.etcd + +# OPTIONAL: kill all docker containers before starting +# docker kill `docker ps --no-trunc -q` + + +# Start the openshift all-in-one server +# (starts a kubernetes master and minion as well as providing the +# origin REST api) +# Uses Host docker socket so that the resulting docker images are +# available on the host. Otherwise the docker image lives only inside +# the docker build container, and goes away when the build container +# goes away. (Currently the resulting image from the build is not +# pushed to any registry). +echo "Launching openshift all-in-one server" +USE_HOST_DOCKER_SOCKET=true $openshift start --listenAddr="0.0.0.0:8080" &> logs/openshift.log & + +sleep 5 + +# Convert template to config +echo "Submitting template json for processing..." +curl -sld @template/template.json http://$OSHOST:8080/osapi/v1beta1/templateConfigs > processed/template.processed.json + +# Deploy the config +$openshift kube -h http://$OSHOST:8080 apply -c processed/template.processed.json + +# Wait for the app container to start up +rc=1 +while [ ! $rc -eq 0 ] +do + echo "Waiting for frontend pod to start..." + $openshift kube list pods + sleep 5 + $openshift kube list pods | grep frontend | grep Running + rc=$? +done + +$openshift kube list services | grep frontend +rc=$? +while [ ! $rc -eq 0 ] +do + echo "Waiting for frontend service to start..." + $openshift kube list services + sleep 5 + $openshift kube list services | grep frontend + rc=$? +done + +# Sometimes the app isn't quite available even though the pod is running, +# wait a little longer. +sleep 5 + +# Confirm the app is running/responsive. +echo "Frontend is available, sending request. Frontend says:" +curl localhost:5432 + +# show build cfgs +echo "Initially no build configurations:" +$openshift kube list buildConfigs + +# define a build cfg +echo "Defining a new build configuration" +$openshift kube create buildConfigs -c buildcfg/buildcfg.json + +# show build cfgs +echo "Build configuration defined:" +$openshift kube list buildConfigs + + +#show no build running +echo "Initially no builds running:" +$openshift kube list builds + + +#Requesting new build +echo "Triggering new build" +curl -s -A "GitHub-Hookshot/github" -H "Content-Type:application/json" -H "X-Github-Event:push" -d @buildinvoke/pushevent.json http://$OSHOST:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github + +# webhook url +#http://$OSHOST:8080/osapi/v1beta1/buildConfigHooks/build100/secret101/github +#cd ~/demofiles/app +#edit app.rb +#git commit -am . ; git push origin master + +#show build running +echo "Build now running: " +$openshift kube list builds + +id=`$openshift kube list builds | grep new | awk '{print $1}'` + +$openshift kube get builds/$id | grep complete +rc=$? +while [ ! $rc -eq 0 ] +do + echo "Waiting for build to complete..." + $openshift kube get builds/$id + sleep 5 + $openshift kube get builds/$id | grep complete + rc=$? +done + +echo "Your new application image is origin_ruby_sample: " +docker images | grep origin-ruby-sample diff --git a/examples/simple-ruby-app/template/template.json b/examples/simple-ruby-app/template/template.json new file mode 100644 index 000000000000..e9c617cefca5 --- /dev/null +++ b/examples/simple-ruby-app/template/template.json @@ -0,0 +1,74 @@ +{ + "id": "ruby-helloworld-sample-template", + "kind": "Template", + "name": "ruby-hello-world-template", + "description": "This example shows how to create a simple ruby application in openshift origin v3", + "parameters": [ + { + "name": "ADMIN_USERNAME", + "description": "administrator username", + "type": "string", + "expression": "admin[A-Z0-9]{3}" + }, + { + "name": "ADMIN_PASSWORD", + "description": "administrator password", + "type": "string", + "expression": "[a-zA-Z0-9]{8}" + }, + { + "name": "DB_PASSWORD", + "description": "", + "type": "string", + "expression": "[a-zA-Z0-9]{8}" + } + ], + "items": [ + { + "id": "frontend", + "kind": "Service", + "apiVersion": "v1beta1", + "port": 5432, + "selector": { + "name": "frontend" + } + }, + { + "id": "frontendController", + "kind": "ReplicationController", + "apiVersion": "v1beta1", + "desiredState": { + "replicas": 1, + "replicaSelector": {"name": "frontend"}, + "podTemplate": { + "desiredState": { + "manifest": { + "version": "v1beta1", + "id": "frontendController", + "containers": [{ + "name": "ruby-helloworld", + "image": "openshift/origin-ruby-sample", + "env": [ + { + "name": "ADMIN_USERNAME", + "value": "${ADMIN_USERNAME}" + }, + { + "name": "ADMIN_PASSWORD", + "value": "${ADMIN_PASSWORD}" + }, + { + "name": "DB_PASSWORD", + "value": "${DB_PASSWORD}" + } + ], + "ports": [{"containerPort": 8080}] + }] + } + }, + "labels": {"name": "frontend"} + }}, + "labels": {"name": "frontend"} + } + ] +}