├── .gitignore ├── .gitmodules ├── Fabric-Model_NGINX-Microservices-Reference-Architecture.png ├── Makefile ├── README.md ├── base_images ├── fake-s3 │ └── Dockerfile └── python │ └── Dockerfile ├── diagram-microservices-reference-architecture-850x600.png ├── docker-compose-dcos.yaml ├── docker-compose-k8s.yaml ├── docker-compose-oss.yaml ├── docker-compose.yaml ├── mra-ingenious.iml └── tests ├── README.md ├── ingenious-tests.js ├── new_upload.jpg ├── nginx.jpg └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | tests/node_modules 2 | tests/package-lock.json 3 | rethinkdb 4 | mysql 5 | dynamo_db 6 | mra-ingenious.iml 7 | 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mra-auth-proxy"] 2 | path = mra-auth-proxy 3 | url = https://github.com/nginxinc/mra-auth-proxy 4 | branch = master 5 | [submodule "mra-album-manager"] 6 | path = mra-album-manager 7 | url = https://github.com/nginxinc/mra-album-manager 8 | branch = master 9 | [submodule "mra-content-service"] 10 | path = mra-content-service 11 | url = https://github.com/nginxinc/mra-content-service 12 | branch = master 13 | [submodule "mra-pages"] 14 | path = mra-pages 15 | url = https://github.com/nginxinc/mra-pages 16 | branch = master 17 | [submodule "mra-photoresizer"] 18 | path = mra-photoresizer 19 | url = https://github.com/nginxinc/mra-photoresizer 20 | branch = master 21 | [submodule "mra-photouploader"] 22 | path = mra-photouploader 23 | url = https://github.com/nginxinc/mra-photouploader 24 | branch = master 25 | [submodule "mra-user-manager"] 26 | path = mra-user-manager 27 | url = https://github.com/nginxinc/mra-user-manager 28 | branch = master 29 | -------------------------------------------------------------------------------- /Fabric-Model_NGINX-Microservices-Reference-Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginxinc/mra-ingenious/9b924c5c1958e11f4148477118e4509627266bd0/Fabric-Model_NGINX-Microservices-Reference-Architecture.png -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker-compose build 3 | 4 | build-oss: 5 | docker-compose build --build-arg USE_NGINX_PLUS_ARG=false 6 | 7 | build-clean: 8 | docker-compose build --no-cache 9 | 10 | build-clean-oss: 11 | docker-compose build --no-cache --build-arg USE_NGINX_PLUS_ARG=false 12 | 13 | run-local: 14 | docker-compose up 15 | 16 | stop: 17 | docker-compose down 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.](https://www.repostatus.org/badges/latest/abandoned.svg)](https://www.repostatus.org/#abandoned) 2 | 3 | # This repository has been archived. There will likely be no further development on the project and security vulnerabilities may be unaddressed. 4 | 5 | # Ingenious 6 | _Ingenious_ is a photo-sharing demo app created by NGINX to show the Fabric Model approach to application development. The app is designed to allow the user to login to a personalized account, and then store, view and delete their own pictures. It also includes a blog in which users can view the latest news and updates within the application. 7 | 8 | ![Fabric Model from Microservices Reference Architecture](Fabric-Model_NGINX-Microservices-Reference-Architecture.png) 9 | 10 | The _Ingenious_ application is built with microservices and utilizes their inherent benefits to generate a robust, stable, independent, polyglot environment. 11 | 12 | Specifically, the app is designed using the [Fabric Model](https://www.nginx.com/blog/microservices-reference-architecture-nginx-fabric-model/) - the most sophisticated architecture in the [MRA](https://www.nginx.com/blog/introducing-the-nginx-microservices-reference-architecture/) - to configure its services. Included in this configuration is an instance of NGINX running in every docker container. This allows for increased security without the typical decreased speed of communication between services. After an initial TLS/SSL handshake, a connection is then established and is able to be reused without any further overhead. 13 | 14 | The Fabric Model suggests a new method of application development. Because NGINX Plus is running on both ends of every connection, capabilities of each service become a function of the app's network rather than capabilities of specific services or servers. NGINX Plus allows this network to be persistent, fast, and stable. 15 | 16 | ![Microservice Reference Architecture diagram of services](diagram-microservices-reference-architecture-850x600.png) 17 | 18 | The _Ingenious_ application employs seven different services in order to create its functionality. 19 | 20 | Pages is the foundational service built in PHP upon which the other services provide functionality. Pages makes calls directly to User Manager, Album Manager, Content Service, and Uploader. 21 | 22 | User Manager is built completely using Python and backed by DynamoDB. It's use is to store and modify user information, allowing the app a login system. Login is done with Google and Facebook through OAuth, but also includes a system for local login when testing the system. 23 | 24 | Album Manager is built using Ruby and backed by MySQL, and allows the user to upload albums of multiple images at once. Album Manager makes calls to the Uploader service and therefore the Resizer to upload and modify images specified by the user. 25 | 26 | The Uploader service is built using Javascript and is used to upload images to an S3 bucket. Uploader then makes calls to the Resizer service with the previously generated id of the image within S3, and Resizer then makes copies of the image with size "Large", "Medium", and "Thumbnail". 27 | 28 | Content Service is built in Go and backed by RethinkDB. The Content Service provides, retrieves, and displays content for the NGINX _Ingenious_ application 29 | 30 | Auth Proxy is a Python app that utilizes Redis' capabilities as a caching server. Making direct connections to both Pages and User Manager, Auth Proxy is used to validate the user's identity. It also serves as the gateway into the application, acting as the only public-facing service within the application. 31 | 32 | ## Prerequisites 33 | 34 | There are a few prerequisites that you need for your development environment 35 | - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 36 | - Notes for Windows users: 37 | - There are bash scripts that are executed by the **CMD** instruction in the Dockerfile. In order for these scripts to run, the `core.autocrlf` must be set to `input` so that the line endings are preserved. The command to set this configuration is: 38 | 39 | ```git config core.autocrlf input``` 40 | 41 | - Docker for Windows must be configured to share the drive, per the discussion [here](https://github.com/docker/compose/issues/4854) 42 | - [docker](https://store.docker.com/search?type=edition&offering=community) 43 | - Note for Linux users: 44 | - The docker-compose file for this repository uses version 3, which requires docker engine 1.13/17.04 or higher, and the docker engine that is installed with Ubuntu 16.04, the version that we used for testing. The appropriate version of docker-compose can be installed with the command below from the [docker-compose site](https://docs.docker.com/compose/install/): 45 | 46 | ```sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose``` 47 | 48 | - For more information, check the compatibility matrix [here](https://docs.docker.com/compose/compose-file/compose-versioning/). 49 | 50 | ## Quick start 51 | You can clone all the repositories of the NGINX _Ingenious_ application using the command below: 52 | ``` 53 | git clone --recurse-submodules https://github.com/nginxinc/mra-ingenious.git 54 | ``` 55 | 56 | Once the repositories are cloned, be sure that you have the latest versions of the submodules by running: 57 | ``` 58 | git pull 59 | ``` 60 | 61 | There are detailed instructions for building the service below, and in order to get started quickly, you can follow these simple 62 | instructions to quickly build the image. 63 | 64 | 0. (Optional) If you don't already have an NGINX Plus license, you can request a temporary developer license 65 | [here](https://www.nginx.com/developer-license/ "Developer License Form"). If you do have a license, then skip to the next step. 66 | 1. Copy your licenses to the **//nginx/ssl** directory for all the services 67 | 2. Modify your _hosts_ file to include fake-s3. It should look like: 68 | ``` 69 | 127.0.0.1 fake-s3 70 | ``` 71 | 3. Go to the **mra-ingenious** directory and run the command `docker-compose up`. 72 | 73 | At this point, you will have the _Ingenious_ application running locally. You can access it in your browser with the url `https://localhost/`. 74 | 75 | To build customized images for the different services or to set other options, please check the specific README for each service. 76 | 77 | Three docker compose files are included with this service: 78 | - [docker-compose.yaml](docker-compose.yaml): to run in containers on the machine where the repository has been cloned 79 | - [docker-compose-k8s.yaml](docker-compose-k8s.yaml): to run on Kubernetes 80 | - [docker-compose-dcos.yaml](docker-compose-dcos.yaml): to run on DC/OS 81 | 82 | ## Makefile 83 | A _Makefile_ has been included in the project to make the process of building and running _Ingenious_ as easy as possible. 84 | You can run `make ` to perform an action in a simple way. 85 | 86 | | Action | Description | 87 | | --------------- | ---------------------------------------------------------------------- | 88 | | build | Builds the Ingenious Docker images with NGINX Plus | 89 | | build-oss | Builds the Ingenious Docker images with NGINX OSS | 90 | | build-clean | Builds the Ingenious Docker images with NGINX Plus without using cache | 91 | | build-clean-oss | Builds the Ingenious Docker images with NGINX OSS without using cache | 92 | | run-local | Runs the Ingenious app in a local environment | 93 | | stop | Stop all the Ingenious app containers | 94 | 95 | ## Going Forward 96 | We intend to maintain and build on this Microservices Reference Architecture by cleaning up things we may have missed, merging your pull requests, and evolving the application as we build microservices technologies. Other examples of our work with microservices include: 97 | 98 | - The [NGINX Plus Ingress Controller](https://github.com/nginxinc/kubernetes-ingress) for [Kubernetes](https://kubernetes.io/docs/concepts/services-networking/ingress/). This is often confused with the ingress controller built using NGINX by the Kubernetes team. You can learn about the difference [here](https://github.com/nginxinc/kubernetes-ingress/issues/190) 99 | - The [nginmesh project](https://github.com/nginmesh), which integrates NGINX Plus as the sidecar in an Istio installation 100 | 101 | You can also learn more about the NGINX Microservices Network Architectures on our blog: 102 | - [The Proxy Model](https://www.nginx.com/blog/microservices-reference-architecture-nginx-proxy-model/) 103 | - [The Router Mesh Model](https://www.nginx.com/blog/microservices-reference-architecture-nginx-router-mesh-model/) 104 | - [The Fabric Model](https://www.nginx.com/blog/microservices-reference-architecture-nginx-fabric-model/) 105 | 106 | As described above, the first release of the NGINX Microservices Reference Architecture, Ingenious, is built using the Fabric Model. Our next major release will include instructions for building the MRA using the Router Mesh Model. 107 | 108 | In the meantime, we have open sourced simple builds of the [Fabric Model](https://github.com/nginxinc/fabric-model-architecture) and [Router Mesh](https://github.com/nginxinc/router-mesh-architecture). These repositories are succinct examples of using the Fabric Model and Router Mesh and focus on the NGINX configuration aspect of each. 109 | 110 | ## A Note About Security 111 | 112 | tl;dr: The Ingenious application is designed to be a _reference architecture_ and should be run only on a local machine or private server. **Do not run in a production environment** 113 | 114 | From the time that it was ideated, the Ingenious application was designed to be a reference architecture for building microservices-based applications using NGINX as the service mesh to facilitate communication to and among each of the microservices. 115 | 116 | As such, our focus has been to develop an application which demonstrates the functionality described above in a simple way. In so doing, the code that we have created has not been tested for use in a production environment. 117 | 118 | In fact, we recommend that the Ingenious application be run on a local machine or private server. If you run the application on a publicly available server, you do so at your own risk. 119 | 120 | That being said, please have fun learning while you build microservices using NGINX! 121 | -------------------------------------------------------------------------------- /base_images/fake-s3/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.5-alpine 2 | MAINTAINER NGINX Professional Services 3 | 4 | # install Ruby 5 | RUN apk update 6 | 7 | # install fake-s3 8 | RUN gem install fakes3 -v 1.2.1 9 | 10 | # run fake-s3 11 | RUN mkdir -p /fakes3_root && \ 12 | which fakes3 13 | ENTRYPOINT ["fakes3"] 14 | CMD ["-r", "/fakes3_root", "-p", "4569"] 15 | EXPOSE 4569 16 | -------------------------------------------------------------------------------- /base_images/python/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.5 2 | 3 | MAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com" 4 | 5 | # Set the debconf front end to Noninteractive 6 | RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections && \ 7 | apt-get update && apt-get install -y -q \ 8 | apt-transport-https \ 9 | libffi-dev \ 10 | libssl-dev \ 11 | lsb-release \ 12 | wget && \ 13 | cd / 14 | 15 | -------------------------------------------------------------------------------- /diagram-microservices-reference-architecture-850x600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginxinc/mra-ingenious/9b924c5c1958e11f4148477118e4509627266bd0/diagram-microservices-reference-architecture-850x600.png -------------------------------------------------------------------------------- /docker-compose-dcos.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | album-manager: 4 | build: 5 | context: mra-album-manager 6 | args: 7 | - CONTAINER_ENGINE=mesos 8 | image: ngrefarch/album-manager:mesos 9 | container_name: album-manager 10 | environment: 11 | - RACK_ENV=production 12 | auth-proxy: 13 | build: 14 | context: mra-auth-proxy 15 | args: 16 | - CONTAINER_ENGINE=mesos 17 | image: ngrefarch/auth-proxy:mesos 18 | container_name: auth-proxy 19 | environment: 20 | - CONTAINER_ENGINE=mesos 21 | content-service: 22 | build: 23 | context: mra-content-service 24 | args: 25 | - CONTAINER_ENGINE=mesos 26 | image: ngrefarch/content-service:mesos 27 | pages: 28 | build: 29 | context: mra-pages 30 | args: 31 | - CONTAINER_ENGINE=mesos 32 | image: ngrefarch/pages:mesos 33 | environment: 34 | - CONTAINER_ENGINE=mesos 35 | container_name: pages 36 | resizer: 37 | build: 38 | context: mra-photoresizer 39 | args: 40 | - CONTAINER_ENGINE=mesos 41 | image: ngrefarch/photoresizer:mesos 42 | container_name: resizer 43 | environment: 44 | - CONTAINER_ENGINE=mesos 45 | uploader: 46 | build: 47 | context: mra-photouploader 48 | args: 49 | - CONTAINER_ENGINE=mesos 50 | image: ngrefarch/photouploader:mesos 51 | container_name: uploader 52 | environment: 53 | - CONTAINER_ENGINE=mesos 54 | user-manager: 55 | build: 56 | context: mra-user-manager 57 | args: 58 | - CONTAINER_ENGINE=mesos 59 | image: ngrefarch/user-manager:mesos 60 | container_name: user-manager 61 | environment: 62 | - CONTAINER_ENGINE=mesos 63 | -------------------------------------------------------------------------------- /docker-compose-k8s.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | album-manager: 4 | build: 5 | context: mra-album-manager 6 | args: 7 | - CONTAINER_ENGINE_ARG=kubernetes 8 | - GOOGLE_CLIENT_ID_ARG= 9 | - FACEBOOK_APP_ID_ARG= 10 | - FACEBOOK_SECRET_KEY_ARG= 11 | image: ngrefarch/album-manager:kubernetes 12 | container_name: album-manager 13 | environment: 14 | - RACK_ENV=production 15 | 16 | auth-proxy: 17 | build: 18 | context: mra-auth-proxy 19 | args: 20 | - CONTAINER_ENGINE=kubernetes 21 | image: ngrefarch/auth-proxy:kubernetes 22 | container_name: auth-proxy 23 | 24 | content-service: 25 | build: 26 | context: mra-content-service 27 | args: 28 | - CONTAINER_ENGINE=kubernetes 29 | image: ngrefarch/content-service:kubernetes 30 | 31 | pages: 32 | build: 33 | context: mra-pages 34 | args: 35 | - CONTAINER_ENGINE=kubernetes 36 | image: ngrefarch/pages:kubernetes 37 | container_name: pages 38 | 39 | resizer: 40 | build: 41 | context: mra-photoresizer 42 | args: 43 | - CONTAINER_ENGINE=kubernetes 44 | image: ngrefarch/photoresizer:kubernetes 45 | container_name: resizer 46 | 47 | uploader: 48 | build: 49 | context: mra-photouploader 50 | args: 51 | - CONTAINER_ENGINE=kubernetes 52 | image: ngrefarch/photouploader:kubernetes 53 | container_name: uploader 54 | 55 | user-manager: 56 | build: 57 | context: mra-user-manager 58 | args: 59 | - CONTAINER_ENGINE=kubernetes 60 | image: ngrefarch/user-manager:kubernetes 61 | container_name: user-manager 62 | -------------------------------------------------------------------------------- /docker-compose-oss.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | 4 | # Album Manager 5 | album-manager: 6 | build: 7 | context: mra-album-manager 8 | args: 9 | - CONTAINER_ENGINE_ARG=local 10 | - USE_NGINX_PLUS_ARG=false 11 | image: ngrefarch/album-manager:local_oss 12 | container_name: album-manager-oss 13 | environment: 14 | - DATABASE_HOST=mysql 15 | - DATABASE_PASSWORD=mra_dev 16 | - DATABASE_USERNAME=root 17 | - NEWRELIC_LICENSE_KEY= 18 | - NETWORK=fabric 19 | - PORT=3306 20 | - UPLOADER_PHOTO=http://localhost/uploader/image/uploads/photos/ 21 | ports: 22 | - "82:443" 23 | links: 24 | - mysql 25 | volumes: 26 | - ./mra-album-manager/app:/usr/src/app 27 | 28 | # Auth Proxy 29 | auth-proxy: 30 | build: 31 | context: mra-auth-proxy 32 | args: 33 | - CONTAINER_ENGINE_ARG=local 34 | - USE_NGINX_PLUS_ARG=false 35 | image: ngrefarch/auth-proxy:local_oss 36 | container_name: auth-proxy-oss 37 | volumes: 38 | - ./mra-auth-proxy/app:/usr/src/app 39 | environment: 40 | - AWS_ACCESS_KEY_ID= 41 | - AWS_REGION=us-west-1 42 | - AWS_SECRET_ACCESS_KEY= 43 | - FLASK_DEBUG=True 44 | - NETWORK=fabric 45 | - PAGES_URL=pages 46 | - REDIS_ENABLED=1 47 | - REDIS_HOST=redis 48 | - REDIS_PORT=6379 49 | - REDIS_TTL=300 50 | - GOOGLE_CLIENT_ID= 51 | - GOOGLE_CLIENT_SECRET= 52 | - FACEBOOK_APP_ID= 53 | - FACEBOOK_APP_SECRET= 54 | ports: 55 | - "80:80" 56 | - "443:443" 57 | links: 58 | - pages 59 | - user-manager 60 | - redis 61 | 62 | # Content DB 63 | content-db: 64 | image: docker.io/rethinkdb:latest 65 | container_name: content-db 66 | ports: 67 | - "28015:28015" 68 | - "28080:8080" 69 | volumes: 70 | - ./rethinkdb:/data 71 | 72 | # Content Service 73 | content-service: 74 | build: 75 | context: mra-content-service 76 | args: 77 | - CONTAINER_ENGINE_ARG=local 78 | - USE_NGINX_PLUS_ARG=false 79 | image: ngrefarch/content-service:local_oss 80 | container_name: content-service-oss 81 | volumes: 82 | - ./mra-content-service/app:/go/src/app 83 | environment: 84 | - NETWORK=fabric 85 | - RETHINKDB_URL=content-db:28015 86 | ports: 87 | - "86:443" 88 | links: 89 | - album-manager 90 | - uploader 91 | - user-manager 92 | 93 | # Dynamo DB 94 | dynamo-db: 95 | image: docker.io/deangiberson/aws-dynamodb-local:latest 96 | container_name: dynamo-db 97 | ports: 98 | - "8000:8000" 99 | volumes: 100 | - ./dynamo_db:/var/dynamodb_local 101 | 102 | # Fake S3 103 | fake-s3: 104 | image: nginxinc/mra-fakes3:1.2.1 105 | container_name: fake-s3 106 | ports: 107 | - "4569:4569" 108 | volumes: 109 | - ./fakes3:/fakes3_root 110 | 111 | # MySQL 112 | mysql: 113 | image: docker.io/mysql:5.7 114 | container_name: mra-mysql 115 | environment: 116 | - MYSQL_ROOT_PASSWORD=mra_dev 117 | ports: 118 | - "3306:3306" 119 | volumes: 120 | - ./mysql:/var/lib/mysql 121 | 122 | # Pages 123 | pages: 124 | build: 125 | context: mra-pages 126 | args: 127 | - CONTAINER_ENGINE_ARG=local 128 | - USE_NGINX_PLUS_ARG=false 129 | image: ngrefarch/pages:local_oss 130 | environment: 131 | - CONTENTSERVICE_ENDPOINT_URL=http://localhost 132 | - CONTENTSERVICE_ARTICLE_PATH=/content-service/v1/content 133 | - AWS_ACCESS_KEY_ID= 134 | - AWS_REGION=us-west-1 135 | - AWS_SECRET_ACCESS_KEY= 136 | - NETWORK=fabric 137 | - S3_BUCKET=mra-images 138 | - PHOTOMANAGER_ALBUM_PATH=/album-manager/albums 139 | - PHOTOMANAGER_CATALOG_PATH=/album-manager/albums 140 | - PHOTOMANAGER_ENDPOINT_URL=http://localhost 141 | - PHOTOMANAGER_IMAGES_PATH=/album-manager/images 142 | - PHOTOUPLOADER_ALBUM_PATH=/uploader/album 143 | - PHOTOUPLOADER_ENDPOINT_URL=uploader:84 144 | - PHOTOUPLOADER_IMAGE_PATH=/uploader/image 145 | - REDIS_CACHE_PORT=6379 146 | - REDIS_CACHE_URL=redis 147 | - SYMFONY_ENV=dev 148 | - USERMANAGER_ENDPOINT_URL=http://localhost 149 | - USERMANAGER_LOCAL_PATH=/user-manager/v1/users 150 | - USERMANAGER_USER_PATH=/user-manager/v1/users 151 | ports: 152 | - "81:443" 153 | links: 154 | - album-manager 155 | - uploader 156 | - user-manager 157 | - redis 158 | volumes: 159 | - ./mra-pages/ingenious-pages/app/Resources:/ingenious-pages/app/Resources 160 | - ./mra-pages/ingenious-pages/app/web:/ingenious-pages/app/web 161 | - ./mra-pages/ingenious-pages/app/config:/ingenious-pages/app/config 162 | - ./mra-pages/ingenious-pages/web:/ingenious-pages/web 163 | - ./mra-pages/ingenious-pages/src:/ingenious-pages/src 164 | - ./mra-pages/ingenious-pages/less-css:/ingenious-pages/less-css 165 | - ./mra-pages/ingenious-pages/tests:/ingenious-pages/tests 166 | container_name: pages-oss 167 | 168 | # Redis 169 | redis: 170 | image: redis:3.2.0 171 | container_name: mra-redis 172 | ports: 173 | - "6379:6379" 174 | 175 | # Resizer 176 | resizer: 177 | build: 178 | context: mra-photoresizer 179 | args: 180 | - CONTAINER_ENGINE_ARG=local 181 | - USE_NGINX_PLUS_ARG=false 182 | image: ngrefarch/photoresizer:local_oss 183 | container_name: resizer-oss 184 | environment: 185 | - AWS_ACCESS_KEY_ID= 186 | - AWS_REGION=us-west-1 187 | - AWS_SECRET_ACCESS_KEY= 188 | - S3_URL=http://fake-s3:4569 189 | - NETWORK=fabric 190 | - REDIS_CACHE_PORT=6379 191 | - REDIS_CACHE_URL=redis 192 | - S3_BUCKET=mra-images 193 | ports: 194 | - "83:443" 195 | links: 196 | - redis 197 | 198 | # Uploader 199 | uploader: 200 | build: 201 | context: mra-photouploader 202 | args: 203 | - CONTAINER_ENGINE_ARG=local 204 | - USE_NGINX_PLUS_ARG=false 205 | image: ngrefarch/photouploader:local_oss 206 | container_name: uploader-oss 207 | volumes: 208 | - ./mra-photouploader/app:/usr/src/app 209 | - ./mra-photouploader/test:/usr/src/test 210 | environment: 211 | - ALBUM_MANAGER_URL=http://localhost/album-manager 212 | - AWS_ACCESS_KEY_ID= 213 | - AWS_REGION=us-west-1 214 | - AWS_SECRET_ACCESS_KEY= 215 | - DEV_MODE=true 216 | - NETWORK=fabric 217 | - RESIZER_URL=http://localhost/resizer/v1/image 218 | - S3_BUCKET=mra-images 219 | - S3_URL=http://fake-s3:4569 220 | ports: 221 | - "84:443" 222 | links: 223 | - album-manager 224 | - resizer 225 | 226 | # User Manager 227 | user-manager: 228 | build: 229 | context: mra-user-manager 230 | args: 231 | - CONTAINER_ENGINE_ARG=local 232 | - USE_NGINX_PLUS_ARG=false 233 | image: ngrefarch/user-manager:local_oss 234 | container_name: user-manager-oss 235 | volumes: 236 | - ./mra-user-manager/app:/usr/src/app 237 | ports: 238 | - "85:443" 239 | environment: 240 | - ALBUM_MANAGER_URL=https://album-manager/albums 241 | - AWS_ACCESS_KEY_ID= 242 | - AWS_DEFAULT_REGION=us-west-1 243 | - AWS_SECRET_ACCESS_KEY= 244 | - DB_ENDPOINT=http://dynamo-db:8000 245 | # - DEV_MODE=true 246 | - NETWORK=fabric 247 | - VERIFY_CERTS=false 248 | links: 249 | - dynamo-db 250 | - album-manager 251 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | 4 | # Album Manager 5 | album-manager: 6 | build: 7 | context: mra-album-manager 8 | args: 9 | - CONTAINER_ENGINE_ARG=local 10 | image: ngrefarch/album-manager:local 11 | container_name: album-manager 12 | environment: 13 | - DATABASE_HOST=mysql 14 | - DATABASE_PASSWORD=mra_dev 15 | - DATABASE_USERNAME=root 16 | - NEWRELIC_LICENSE_KEY= 17 | - NETWORK=fabric 18 | - CONTAINER_ENGINE=local 19 | - PORT=3306 20 | - UPLOADER_PHOTO=http://localhost/uploader/image/uploads/photos/ 21 | ports: 22 | - "82:443" 23 | links: 24 | - mysql 25 | depends_on: 26 | - mysql 27 | volumes: 28 | - ./mra-album-manager/app:/usr/src/app 29 | 30 | # Auth Proxy 31 | auth-proxy: 32 | build: 33 | context: mra-auth-proxy 34 | args: 35 | - CONTAINER_ENGINE_ARG=local 36 | image: ngrefarch/auth-proxy:local 37 | container_name: auth-proxy 38 | volumes: 39 | - ./mra-auth-proxy/app:/usr/src/app 40 | environment: 41 | - AWS_ACCESS_KEY_ID= 42 | - AWS_REGION=us-west-1 43 | - AWS_SECRET_ACCESS_KEY= 44 | - FLASK_DEBUG=True 45 | - NETWORK=fabric 46 | - CONTAINER_ENGINE=local 47 | - PAGES_URL=pages 48 | - REDIS_ENABLED=1 49 | - REDIS_HOST=redis 50 | - REDIS_PORT=6379 51 | - REDIS_TTL=300 52 | - GOOGLE_CLIENT_ID= 53 | - GOOGLE_CLIENT_SECRET= 54 | - FACEBOOK_APP_ID= 55 | - FACEBOOK_APP_SECRET= 56 | ports: 57 | - "80:80" 58 | - "443:443" 59 | links: 60 | - pages 61 | - user-manager 62 | - redis 63 | depends_on: 64 | - pages 65 | - user-manager 66 | - redis 67 | 68 | # Content DB 69 | content-db: 70 | image: docker.io/rethinkdb:latest 71 | container_name: content-db 72 | ports: 73 | - "28015:28015" 74 | - "28080:8080" 75 | volumes: 76 | - ./rethinkdb:/data 77 | 78 | # Content Service 79 | content-service: 80 | build: 81 | context: mra-content-service 82 | args: 83 | - CONTAINER_ENGINE_ARG=local 84 | image: ngrefarch/content-service:local 85 | container_name: content-service 86 | volumes: 87 | - ./mra-content-service/app:/go/src/app 88 | environment: 89 | - NETWORK=fabric 90 | - CONTAINER_ENGINE=local 91 | - RETHINKDB_URL=content-db:28015 92 | - ALBUMS_PATH=/album-manager/albums 93 | - ALBUM_MANAGER_HOST=http://localhost 94 | ports: 95 | - "86:443" 96 | links: 97 | - album-manager 98 | - uploader 99 | - user-manager 100 | depends_on: 101 | - content-db 102 | 103 | # Dynamo DB 104 | dynamo-db: 105 | image: docker.io/deangiberson/aws-dynamodb-local:latest 106 | container_name: dynamo-db 107 | ports: 108 | - "8000:8000" 109 | volumes: 110 | - ./dynamo_db:/var/dynamodb_local 111 | 112 | # Fake S3 113 | fake-s3: 114 | image: nginxinc/mra-fakes3:1.2.1 115 | container_name: fake-s3 116 | ports: 117 | - "4569:4569" 118 | volumes: 119 | - ./fakes3:/fakes3_root 120 | 121 | # MySQL 122 | mysql: 123 | image: docker.io/mysql:5.7 124 | container_name: mra-mysql 125 | environment: 126 | - MYSQL_ROOT_PASSWORD=mra_dev 127 | ports: 128 | - "3306:3306" 129 | volumes: 130 | - ./mysql:/var/lib/mysql 131 | 132 | # Pages 133 | pages: 134 | build: 135 | context: mra-pages 136 | args: 137 | - CONTAINER_ENGINE_ARG=local 138 | image: ngrefarch/pages:local 139 | environment: 140 | - AWS_ACCESS_KEY_ID= 141 | - AWS_REGION=us-west-1 142 | - AWS_SECRET_ACCESS_KEY= 143 | - CONTENTSERVICE_ARTICLE_PATH=/content-service/v1/content 144 | - CONTENTSERVICE_ENDPOINT_URL=http://localhost 145 | - GOOGLE_CLIENT_ID= 146 | - NETWORK=fabric 147 | - CONTAINER_ENGINE=local 148 | - S3_BUCKET=mra-images 149 | - ALBUMMANAGER_ALBUM_PATH=/album-manager/albums 150 | - ALBUMMANAGER_CATALOG_PATH=/album-manager/albums 151 | - ALBUMMANAGER_ENDPOINT_URL=http://localhost 152 | - ALBUMMANAGER_IMAGES_PATH=/album-manager/images 153 | - ALBUMMANAGER_PUBLIC_PATH=/album-manager/public 154 | - PHOTOUPLOADER_ALBUM_PATH=/uploader/album 155 | - PHOTOUPLOADER_ENDPOINT_URL=http://localhost 156 | - PHOTOUPLOADER_IMAGE_PATH=/uploader/image 157 | - REDIS_CACHE_PORT=6379 158 | - REDIS_CACHE_URL=redis 159 | - SYMFONY_ENV=dev 160 | - USERMANAGER_ENDPOINT_URL=http://localhost 161 | - USERMANAGER_LOCAL_PATH=/user-manager/v1/users 162 | - USERMANAGER_USER_PATH=/user-manager/v1/users 163 | ports: 164 | - "81:443" 165 | links: 166 | - album-manager 167 | - uploader 168 | - user-manager 169 | - redis 170 | volumes: 171 | - ./mra-pages/ingenious-pages/app/Resources:/ingenious-pages/app/Resources 172 | - ./mra-pages/ingenious-pages/app/web:/ingenious-pages/app/web 173 | - ./mra-pages/ingenious-pages/app/config:/ingenious-pages/app/config 174 | - ./mra-pages/ingenious-pages/web:/ingenious-pages/web 175 | - ./mra-pages/ingenious-pages/src:/ingenious-pages/src 176 | - ./mra-pages/ingenious-pages/less-css:/ingenious-pages/less-css 177 | - ./mra-pages/ingenious-pages/tests:/ingenious-pages/tests 178 | - ./mra-pages/ingenious-pages/Insert.php:/ingenious-pages/Insert.php 179 | container_name: pages 180 | depends_on: 181 | - album-manager 182 | - uploader 183 | - user-manager 184 | - redis 185 | 186 | 187 | # Redis 188 | redis: 189 | image: redis:3.2.0 190 | container_name: mra-redis 191 | ports: 192 | - "6379:6379" 193 | 194 | # Resizer 195 | resizer: 196 | build: 197 | context: mra-photoresizer 198 | args: 199 | - CONTAINER_ENGINE_ARG=local 200 | image: ngrefarch/photoresizer:local 201 | container_name: resizer 202 | environment: 203 | - AWS_ACCESS_KEY_ID= 204 | - AWS_SECRET_ACCESS_KEY= 205 | - S3_URL=http://fake-s3:4569 206 | - NETWORK=fabric 207 | - CONTAINER_ENGINE=local 208 | - REDIS_CACHE_PORT=6379 209 | - REDIS_CACHE_URL=redis 210 | - S3_BUCKET=mra-images 211 | - CPU_THRESHOLD=0.9 212 | - MEMORY_THRESHOLD=0.8 213 | - DISK_THRESHOLD=0.05 214 | - LARGE_IMAGE_SIZE=-1 215 | - MEDIUM_IMAGE_SIZE=1280 216 | - THUMB_IMAGE_SIZE=128 217 | ports: 218 | - "83:443" 219 | links: 220 | - redis 221 | - fake-s3 222 | depends_on: 223 | - fake-s3 224 | 225 | # Uploader 226 | uploader: 227 | build: 228 | context: mra-photouploader 229 | args: 230 | - CONTAINER_ENGINE_ARG=local 231 | image: ngrefarch/photouploader:local 232 | container_name: uploader 233 | volumes: 234 | - ./mra-photouploader/app:/usr/src/app 235 | environment: 236 | - ALBUM_MANAGER_URL=http://localhost/album-manager 237 | - AWS_ACCESS_KEY_ID= 238 | - AWS_REGION=us-west-1 239 | - AWS_SECRET_ACCESS_KEY= 240 | - DEV_MODE=true 241 | - NETWORK=fabric 242 | - CONTAINER_ENGINE=local 243 | - RESIZER_URL=http://localhost/resizer/v1/image 244 | - S3_BUCKET=mra-images 245 | - S3_URL=http://fake-s3:4569 246 | ports: 247 | - "84:443" 248 | links: 249 | - album-manager 250 | - resizer 251 | depends_on: 252 | - album-manager 253 | - resizer 254 | 255 | 256 | # User Manager 257 | user-manager: 258 | build: 259 | context: mra-user-manager 260 | args: 261 | - CONTAINER_ENGINE_ARG=local 262 | image: ngrefarch/user-manager:local 263 | container_name: user-manager 264 | volumes: 265 | - ./mra-user-manager/app:/usr/src/app 266 | ports: 267 | - "85:443" 268 | environment: 269 | - ALBUM_MANAGER_URL=https://album-manager/albums 270 | - AWS_ACCESS_KEY_ID= 271 | - AWS_DEFAULT_REGION=us-west-1 272 | - AWS_SECRET_ACCESS_KEY= 273 | - DB_ENDPOINT=http://dynamo-db:8000 274 | # - DEV_MODE=true 275 | - NETWORK=fabric 276 | - CONTAINER_ENGINE=local 277 | - VERIFY_CERTS=false 278 | links: 279 | - dynamo-db 280 | - album-manager 281 | depends_on: 282 | - dynamo-db 283 | - album-manager 284 | -------------------------------------------------------------------------------- /mra-ingenious.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Testing the Ingenious Application 2 | 3 | We use selenium-webdriver to run a full suite of tests against the Ingenious Application. 4 | 5 | Node is required to run the tests and you can find instructions to install Node [here](https://docs.npmjs.com/getting-started/installing-node). 6 | 7 | Once node is installed, additional dependencies must be downloaded before running the tests. Node will use the [package.json](package.json) file to download [selenium-webdriver](https://www.npmjs.com/package/selenium-webdriver) and [geckodriver](https://www.npmjs.com/package/geckodriver) to a directory called _node_modules_. Run the `npm install` command to get the dependencies. 8 | 9 | With the dependencies in place, the tests can be run using the command: 10 | 11 | 12 | ``` 13 | node ingenious-tests.js -i /mra-ingenious/tests/nginx.jpg 14 | ``` 15 | 16 | __ is the absolute path to the directory where the repository has been cloned. Selenium Webdriver requires an absolute path; a relative path will not work. 17 | 18 | There following usage for the ingenious-tests.js script can be output with the command `node ingenious-tests.js -h` 19 | 20 | ``` 21 | --- You must set the image path with the -i parameter --- 22 | ----- Help For Ingenious Test Script ----- 23 | 24 | Example: 25 | 26 | -- using default parameters: 27 | node ingenious-tests.js 28 | 29 | -- using custom host and image: 30 | node ingenious-tests.js -u http(s):// -i 31 | 32 | Options: 33 | 34 | -h display this help 35 | -u URL of the host to test against, you must specify the scheme: ex. https://k8s.mra.nginxps.com, defaults to http://localhost 36 | -i Absolute path to the image file on your system: ex. /Users/username/repos/mra-ingenious/tests/nginx.jpg 37 | ``` 38 | 39 | The _-u_ switch allows the option to specify an URL against which to run the tests. The default value is _https://localhost_ 40 | 41 | Upon executing the tests, a Firefox browser window will open and the tests will begin to traverse the site and act on the elements of the pages. If there are any errors, they will appear in the console. -------------------------------------------------------------------------------- /tests/ingenious-tests.js: -------------------------------------------------------------------------------- 1 | const {Builder, By, Key, logging, until, Assertion} = require('selenium-webdriver'); 2 | require('geckodriver') 3 | const assert = require('assert'); 4 | const test = require('selenium-webdriver/testing'); 5 | const startTime = new Date(); 6 | const username = 'test_' + startTime.getTime() + '@nginx.com'; 7 | const password = 'testing123'; 8 | const userFirstLast = 'Test User'; 9 | var imageFilePath; 10 | var host = 'https://localhost'; 11 | 12 | // TODO: parse arguments and use named parameters to set imageFilePath and host 13 | 14 | process.argv.forEach(function(arg, index) { 15 | switch (arg) { 16 | case '-h': 17 | logHelp(); 18 | process.exit(1); 19 | case '-u': 20 | host = trimTrailingSlash(process.argv[index + 1]); 21 | break; 22 | case '-i': 23 | imageFilePath = process.argv[index + 1]; 24 | break; 25 | } 26 | }); 27 | 28 | if (!imageFilePath) { 29 | console.log('\x1b[31m--- You must set the image path with the -i parameter ---\x1b[0m'); 30 | logHelp(); 31 | process.exit(1); 32 | } 33 | 34 | console.log('\x1b[32mStarting Ingenious tests with parameters:'); 35 | console.log('\t- host: ' + host); 36 | console.log('\t- image ' + imageFilePath + '\x1b[0m'); 37 | 38 | var baseElement; 39 | 40 | let driver = new Builder() 41 | .withCapabilities( 42 | { 43 | 'acceptInsecureCerts':true, 44 | 'pageLoadStrategy' : 'eager', 45 | 'moz:firefoxOptions' : { 46 | 'log': { 47 | 'level' : 'trace' 48 | } 49 | }}) 50 | .forBrowser('firefox') 51 | .build(); 52 | 53 | driver.manage().timeouts().implicitlyWait(5000); 54 | 55 | // HOME PAGE 56 | driver.get(host).then(() => { 57 | console.log('- Getting the home page\n'); 58 | driver.wait(until.titleIs('Ingenious Photos')); 59 | driver.wait(until.elementLocated(By.id('top'))); 60 | driver.wait(until.elementLocated(By.className('hero-banner-title'))); 61 | }).then(() => { 62 | console.log('- Checking the page title\n'); 63 | baseElement = driver.findElement(By.id('top')); 64 | 65 | baseElement.getText().then(function(text) { 66 | console.log('--' + text); 67 | }); 68 | 69 | baseElement.findElement(By.className('hero-banner-title')).getText().then(function(text) { 70 | console.log('--' + text + '\n'); 71 | assert.equal(text, 'Ingenious'); 72 | }); 73 | }).then(() => { 74 | console.log('- Verifying the existence of the articles on the home page'); 75 | 76 | driver.findElement(By.id('articles')).findElements(By.css('.single-post')).then(function(result) { 77 | console.log('-- There are ' + result.length + ' articles\n'); 78 | // assert.ok(result.length > 0, 'There are not enough articles'); 79 | }); 80 | }).then(() => { 81 | console.log('- Checking the about page'); 82 | 83 | driver.get(host + '/about'); 84 | 85 | baseElement = driver.findElement(By.css('#about .page-header .page-title')); 86 | 87 | baseElement.getText().then(function(text) { 88 | console.log('-- ' + text + '\n'); 89 | assert.equal(text, 'About Microservices', 'The values are not equal'); 90 | }); 91 | }).then(() => { 92 | console.log('- Checking the account page as an unauthenticated user'); 93 | 94 | driver.get(host + '/account'); 95 | 96 | driver.findElement(By.id('user-login')).then(function(result) { 97 | console.log('-- Loaded account page and found the login form\n'); 98 | }); 99 | }).then(() => { 100 | console.log('- Checking the myphotos page as an unauthenticated user'); 101 | 102 | driver.get(host + '/myphotos'); 103 | 104 | driver.findElement(By.id('user-login')).then(function(result) { 105 | console.log('-- Loaded photos page and found the login form\n'); 106 | }); 107 | 108 | }).then(() => { 109 | console.log('- Creating a new user for authenticated test'); 110 | driver.get(host + '/login'); 111 | 112 | baseElement = verifyLoginFormExists(); 113 | populateLoginFormFields(baseElement); 114 | driver.findElement(By.id('login-form-button')).click(); 115 | 116 | console.log('- Updating the user account with ' + userFirstLast + '\n'); 117 | getAccountForm().findElement(By.id('name')).sendKeys(userFirstLast); 118 | driver.findElement(By.id('update-account-button')).click(); 119 | }).then(() => { 120 | console.log('- Checking account update'); 121 | driver.findElement(By.id('name')).getAttribute('value').then(function(accountName) { 122 | console.log('-- Comparing ' + accountName + ' with ' + userFirstLast + '\n'); 123 | assert.ok(userFirstLast == accountName, "The account update operation failed"); 124 | }); 125 | 126 | }).then(() => { 127 | console.log('- Starting photo upload tests'); 128 | driver.get(host + '/myphotos'); 129 | driver.wait(until.elementLocated(By.css('#albums'))); 130 | baseElement = driver.findElement(By.id('albums')); 131 | baseElement.findElement(By.css('.page-title')).getText().then(function(albumPage) { 132 | console.log('- Validating album page'); 133 | assert.ok(albumPage.toUpperCase() == 'MY ALBUMS', 'The album page titie is incorrect. Expected "My Albums" got "' + albumPage + '"'); 134 | driver.findElement(By.css('.right-nav .right-nav-items')).isDisplayed().then(function(isDisplayed) { 135 | console.log('Is the right nav visible? ' + isDisplayed + '\n'); 136 | assert.ok(!isDisplayed, 'The right nav is visible'); 137 | }) 138 | }).then(() => { 139 | console.log('- Starting Album Management'); 140 | driver.findElement(By.css('.right-nav-btn a')).click().then(() => { 141 | driver.findElement(By.css('.right-nav.nav-close.nav-open')).then(rightNav => { 142 | driver.wait(until.elementIsEnabled(driver.findElement(By.css('.add-album-btn')))).then(albumButton => { 143 | driver.wait(until.elementIsVisible(albumButton)); 144 | }).then(() => { 145 | console.log('-- Adding a new album') 146 | driver.findElement(By.css('.add-album-btn')).click().then(() => { 147 | console.log('--- Add album button clicked'); 148 | var newAlbumForm = driver.findElement(By.id('album-upload')); 149 | driver.wait(newAlbumForm); 150 | populateNewAlbumFormFields(newAlbumForm); 151 | driver.findElement(By.css('button[type="submit"]')).click(); 152 | }).then(() => { 153 | console.log('-- Verify that the album was created and the image was loaded'); 154 | var albumLoadingElement = driver.findElement(By.id('album-loading')); 155 | driver.wait(until.elementLocated(By.id('upload-thumb-2'))); 156 | albumLoadingElement.getAttribute('innerHTML').then(innerHTML => { 157 | driver.findElements(By.css('#album-upload-thumbs .upload-thumb')).then(uploadedThumbs => { 158 | assert.ok(uploadedThumbs.length > 0, 'No thumbnails are displayed'); 159 | }); 160 | driver.findElement(By.css('.add-album .cancel-upload')).click(); 161 | }); 162 | }).then(() => { 163 | driver.findElement(By.css('.right-nav-items .nav-item .add-photo-btn')).click().then(() => { 164 | console.log('-- Adding photo'); 165 | driver.findElement(By.id('photo-upload')).then(photoUploadForm => { 166 | console.log('--- found photoUploadForm '); 167 | driver.wait(until.elementIsEnabled( 168 | photoUploadForm.findElement(By.xpath('//option[text()="album_' + startTime.getTime() + '"]')) 169 | )).then(optionElement => { 170 | console.log(optionElement); 171 | driver.sleep(5000); 172 | console.log('--- looking for option element'); 173 | driver.wait(until.elementIsVisible(optionElement)); 174 | return optionElement; 175 | }).then(optionElement => { 176 | console.log('--- selecting option'); 177 | optionElement.click(); 178 | }); 179 | }); 180 | }); 181 | }).then(() => { 182 | console.log('-- Add image ' + imageFilePath) 183 | driver.findElement(By.css('#photo-upload #add-photo-input')).sendKeys(imageFilePath); 184 | }).then(() => { 185 | console.log('-- Submit photo update'); 186 | driver.findElement(By.css('#photo-upload #add-photo-button')).click(); 187 | }).then(() => { 188 | console.log('-- Wait for upload'); 189 | driver.findElement(By.id('photos-loading')).then(element => { 190 | driver.wait(until.elementTextMatches(element, new RegExp('.*1 of 1.*', 'i'))); 191 | }); 192 | }).then(() => { 193 | console.log('-- Close Add Photo Form') 194 | driver.findElement(By.css('.add-photo .cancel-upload')).click(); 195 | }).then(() => { 196 | // driver.navigate().refresh(); 197 | console.log('-- Create post'); 198 | rightNav.findElement(By.css('.create-post-btn')).click().then(() => { 199 | driver.findElement(By.id('post-title')).sendKeys('Test Post ' + startTime.getTime()); 200 | driver.findElement(By.id('post-body')).sendKeys('This is the body for a test post. It can be much longer, but for now it is short'); 201 | driver.findElement(By.id('post-author')).sendKeys('author field is required by the form. we should change that'); 202 | // driver.findElement(By.id('post-photo')).sendKeys('Photo field is required by the form we should change that'); 203 | // console.log('--- Getting dropdown'); 204 | // driver.findElement(By.id('add-post-album-id')).then(albumSelect => { 205 | // console.log('---- found select element '); 206 | // driver.wait(until.elementIsEnabled( 207 | // albumSelect.findElement(By.xpath('//option[text()="album_' + startTime.getTime() + '"]')) 208 | // )).then(albumElement => { 209 | // console.log('---- looking for album option element'); 210 | // driver.wait(until.elementIsVisible(albumElement)); 211 | // return albumElement; 212 | // }).then(albumElement => { 213 | // console.log('---- selecting album'); 214 | // albumElement.click(); 215 | // }); 216 | // }); 217 | driver.findElement(By.id('post-location')).sendKeys('San Francisco'); 218 | driver.findElement(By.id('post-extract')).sendKeys('This is a short description of the post'); 219 | driver.findElement(By.id('add-post-button')).click(); 220 | }); 221 | }).then(() => { 222 | driver.sleep(5000); 223 | console.log('-- Confirm that the post was uploaded'); 224 | driver.findElement(By.id('post-loading')).getText().then(uploadMessage => { 225 | console.log('--- uploaded response message is: ' + uploadMessage); 226 | assert.ok(uploadMessage, 'There is no upload message'); 227 | assert.equal(uploadMessage.toLowerCase(), 'post upload complete', 'The upload message does not indicate success'); 228 | }); 229 | }).then(() => { 230 | console.log('-- Close Create Post Form'); 231 | driver.findElement(By.css('.create-post .cancel-upload')).click(); 232 | }).then(() => { 233 | console.log('-- Start Delete Album: album_' + startTime.getTime()); 234 | rightNav.findElement(By.css('.delete-album-btn')).click(); 235 | }).then(() => { 236 | console.log('-- Select the dropdown option: album_' + startTime.getTime()); 237 | driver.findElement(By.xpath('//select[@id="delete-album-id"]/option[text()="album_' + startTime.getTime() + '"]')).click(); 238 | }).then(t => { 239 | console.log('-- Selected album'); 240 | driver.findElement(By.xpath('//form[@id="album-delete"]//button[text()="Delete Album"]')).click(); 241 | }).then(() => { 242 | console.log('-- Deleted album'); 243 | driver.findElement(By.css('.delete-album .cancel-upload')).click(); 244 | }).then(() => { 245 | driver.get(host + ''); 246 | // driver.findElement(By.xpath('//div[contains(@class, "article-container")]//h2[contains(@class, "entry-title")]/a[text()="Test Post ' + startTime.getTime() + '"]')) 247 | // .getText().then(title => { 248 | // console.log('-- Confirm post exists'); 249 | // console.log('--- Found post title: "' + title + '"'); 250 | // assert.equal(title.toLowerCase(), 'test post ' + startTime.getTime(), "The title doesn't match the post"); 251 | // }); 252 | }); 253 | }); 254 | }); 255 | }); 256 | }); 257 | }).then(() => { 258 | console.log('\n Tests Complete! \n'); 259 | }); 260 | 261 | 262 | driver.quit(); 263 | 264 | function getUserLoginForm() { 265 | return driver.findElement(By.id('user-login')); 266 | } 267 | 268 | function getAccountForm() { 269 | return driver.findElement(By.id('account-manager')); 270 | } 271 | 272 | function verifyLoginFormExists() { 273 | var loginForm = getUserLoginForm(); 274 | loginForm.then(function(result){ 275 | assert.ok(result, 'there is no user-login form'); 276 | }); 277 | 278 | return loginForm; 279 | } 280 | 281 | function populateNewAlbumFormFields(newAlbumForm) { 282 | console.log('--- creating a new album: album_' + startTime.getTime() + '\n'); 283 | newAlbumForm.findElement(By.id('album-name')).sendKeys('album_'+startTime.getTime()); 284 | newAlbumForm.findElement(By.id('album-photo-input')).sendKeys(imageFilePath); 285 | } 286 | 287 | function populateLoginFormFields(loginForm) { 288 | console.log('-- creating user for email: ' + username + '\n'); 289 | loginForm.findElement(By.id('email')).sendKeys(username); 290 | loginForm.findElement(By.id('password')).sendKeys(password); 291 | } 292 | 293 | function trimTrailingSlash(stringToTrim) { 294 | return (stringToTrim.charAt(stringToTrim.length) == '/') ? 295 | stringToTrim.substring(0, stringToTrim.length - 1) : stringToTrim; 296 | } 297 | 298 | function logHelp() { 299 | console.log('----- Help For Ingenious Test Script -----'); 300 | console.log(); 301 | console.log('Example:\n'); 302 | console.log('-- using default parameters:') 303 | console.log(' node ingenious-tests.js\n'); 304 | console.log('-- using custom host and image:'); 305 | console.log(' node ingenious-tests.js -u http(s):// -i \n'); 306 | console.log('Options:\n'); 307 | console.log(' -h display this help'); 308 | console.log(' -u URL of the host to test against, you must specify the scheme: ex. https://k8s.mra.nginxps.com, defaults to http://localhost'); 309 | console.log(' -i Absolute path to the image file on your system: ex. /Users/username/repos/mra-ingenious/tests/nginx.jpg\n\n'); 310 | } 311 | -------------------------------------------------------------------------------- /tests/new_upload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginxinc/mra-ingenious/9b924c5c1958e11f4148477118e4509627266bd0/tests/new_upload.jpg -------------------------------------------------------------------------------- /tests/nginx.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nginxinc/mra-ingenious/9b924c5c1958e11f4148477118e4509627266bd0/tests/nginx.jpg -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "selenium-webdriver": "^3.6.0", 4 | "geckodriver": "^1.10.0" 5 | } 6 | } 7 | --------------------------------------------------------------------------------