diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..676357f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,8 @@ +module.exports = { + "extends": "airbnb-base", + "plugins":["jasmine"], + "env": { + "jasmine": true, + "jest":true + } +}; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5a23aa6..15f2ae1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /node_modules/* +package-lock.json +coverage/* +junit.xml \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f0a9cea --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest All", + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + "args": ["--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}\\server.js" + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 475b72d..c36d8b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,4 +14,4 @@ WORKDIR /web RUN npm install EXPOSE 8080 -ENTRYPOINT ["nodejs", "./index.js"] +ENTRYPOINT ["npm", "start"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..cfc4a27 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,114 @@ +def namespace = "example-hello-world" +def label = "mypod-${UUID.randomUUID().toString()}" +podTemplate(label: label, serviceAccount: "jenkins", cloud: "example", yaml: """ +apiVersion: v1 +kind: Pod +metadata: + name: jenkins-slave-nodejs +spec: + containers: + - name: jnlp + image: tlitovsk/jenkins-jnlp:latest + imagePullPolicy: Always + env: + - name: DOCKER_HOST + value: tcp://localhost:2375 + - name: dind-daemon + image: docker:18-dind + resources: + requests: + cpu: 20m + memory: 512Mi + securityContext: + privileged: true + volumeMounts: + - name: docker-graph-storage + mountPath: /var/lib/docker + volumes: + - name: docker-graph-storage + emptyDir: {} +""" +) +{ + node(label) { + try { + stage('Checkout'){ + checkout scm + } + + stage('Lint Dockerfiles') + { + sh 'dockerlint Dockerfile' + sh 'dockerlint TestContainer/Dockerfile' + } + stage('Test'){ + env.NODE_ENV = "test" + print "Environment will be : ${env.NODE_ENV}" + sh 'node -v' + sh 'npm install' + sh 'npm test' + junit('junit.xml') + } + if (env.BRANCH_NAME != 'master') { + stage ('SonarQube analysis') + { + withSonarQubeEnv('QubeR') { + // requires SonarQube Scanner for Maven 3.2+ + sh '/usr/lib/node_modules/sonarqube-scanner/dist/bin/sonar-scanner' + } + } + } + stage('Build'){ + docker.withRegistry('https://registry.hub.docker.com', 'dockerhub'){ + shortCommit = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() + def hello_image = docker.build("tlitovsk/kubernetes-nodejs-helloworld:${shortCommit}") + currentBuild.result = "SUCCESS" + hello_image.push() + } + } + if (env.BRANCH_NAME != 'master') { + stage('Integration tests'){ + shortCommit = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() + testNamespace = "${namespace}-${shortCommit}-${BUILD_NUMBER}" + sh "kubectl get deployments --namespace=${testNamespace}" + sh "cd deployment \ + && sed -i s/ver1/${shortCommit}/ hello-2.yaml \ + && kubectl delete ns ${testNamespace} || true \ + && kubectl create ns ${testNamespace}\ + && kubectl create -f hello-3-service.yaml --namespace=${testNamespace}\ + && kubectl create -f hello-2.yaml --namespace=${testNamespace}" + sh "sleep 3" + sh "kubectl rollout status deployment/hello-deployment --namespace=${testNamespace}" + sh "curl http://hello-service.${testNamespace}.svc.cluster.local:8080" + sh "curl http://hello-service.${testNamespace}.svc.cluster.local:8080/world" + sh "curl http://hello-service.${testNamespace}.svc.cluster.local:8080/vodafone" // + sh "kubectl delete ns ${testNamespace}" + + } + }else{ + stage('Deploy') + { + shortCommit = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() + sh "kubectl get deployments --namespace=${namespace}" + sh "cd deployment \ + && sed -i s/ver1/${shortCommit}/ hello-2.yaml \ + && kubectl apply -f hello-2.yaml --namespace=${namespace}" + sh "kubectl rollout status deployment/hello-deployment --namespace=${namespace}" + + } + stage('Verify') + { + sh "curl https://test.devtrails.co.nz/hello" + } + } + } + catch (err) { + currentBuild.result = "FAILURE" + shortCommit = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim() + testNamespace = "${namespace}-${shortCommit}-${BUILD_NUMBER}" + sh 'kubectl delete ns ${testNamespace} || true' + throw err + } + + } +} \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 7dae9a3..0000000 --- a/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -NAME ?= node-hello - -install: - npm install - -build: - docker build -t $(NAME) . - -bash: build - docker run -it --rm --entrypoint /bin/bash $(NAME) - -run: build - docker run -i -t -p 43567:8080 -d $(NAME) - -test: - curl localhost:43567 - -clean: - rm -rf node_modules - - -.PHONY: install build run test clean diff --git a/README.md b/README.md index 8e7428e..5129b56 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,19 @@ -# Docker node.js example app +# Kubernetes node.js example app -To build a container: -``` -$ make build -``` +## To build a container: +docker build -t tlitovsk/kubernetes-nodejs-helloworld:ver1 . +docker build -t tlitovsk/kubernetes-nodejs-helloworld:ver2 . + +## Push it +docker push tlitovsk/kubernetes-nodejs-helloworld:ver1 +docker push tlitovsk/kubernetes-nodejs-helloworld:ver2 + +## URL access inside the container +https://159.65.132.157/k8s/clusters/c-swqn4/api/v1/namespaces/example/services/hello-service:8080/proxy/ +https://159.65.132.157/k8s/clusters/c-swqn4/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ +https://128.199.223.176/hello + +## To build the test container +docker build --rm -t tlitovsk/jenkins-jnlp:latest . -Following command will start Docker image in background -``` -$ make run -``` -you can test it with: -``` -$ make test -``` -or simply run: -``` -curl -v localhost:43567 -``` diff --git a/TestContainer/Dockerfile b/TestContainer/Dockerfile new file mode 100644 index 0000000..a7da4f6 --- /dev/null +++ b/TestContainer/Dockerfile @@ -0,0 +1,21 @@ +FROM jenkins/jnlp-slave:latest +ARG DEBIAN_FRONTEND=noninteractive + +USER root + +RUN apt-get update && apt-get install --no-install-recommends -y curl dirmngr gnupg apt-transport-https ca-certificates +RUN curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -\ + && echo 'deb https://deb.nodesource.com/node_8.x stretch main' > /etc/apt/sources.list.d/nodesource.list\ + && curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - \ + && echo 'deb https://download.docker.com/linux/debian stretch stable' > /etc/apt/sources.list.d/docker.list \ + && curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \ + && echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' > /etc/apt/sources.list.d/kubernetes.list \ + && apt-get update\ + && apt-get install -y nodejs docker-ce kubectl && apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN npm install -g dockerlint sonarqube-scanner + +USER jenkins + +ENTRYPOINT ["jenkins-slave"] +#docker build --rm -t tlitovsk/jenkins-jnlp:latest . \ No newline at end of file diff --git a/deployment/hello-1.yaml b/deployment/hello-1.yaml new file mode 100644 index 0000000..ca0af2d --- /dev/null +++ b/deployment/hello-1.yaml @@ -0,0 +1,18 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-deployment +spec: + selector: + matchLabels: + app: hello-world + template: + metadata: + labels: + app: hello-world + spec: + containers: + - name: hello-world + image: tlitovsk/kubernetes-nodejs-helloworld:ver1 + imagePullPolicy: Always + \ No newline at end of file diff --git a/deployment/hello-2.yaml b/deployment/hello-2.yaml new file mode 100644 index 0000000..cd9c280 --- /dev/null +++ b/deployment/hello-2.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-deployment +spec: + replicas: 3 + selector: + matchLabels: + app: hello-world + template: + metadata: + labels: + app: hello-world + spec: + containers: + - name: hello-world + image: tlitovsk/kubernetes-nodejs-helloworld:ver1 + imagePullPolicy: Always + ports: + - containerPort: 8080 + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 3 \ No newline at end of file diff --git a/deployment/hello-3-service.yaml b/deployment/hello-3-service.yaml new file mode 100644 index 0000000..a4e0325 --- /dev/null +++ b/deployment/hello-3-service.yaml @@ -0,0 +1,11 @@ +kind: Service +apiVersion: v1 +metadata: + name: hello-service +spec: + selector: + app: hello-world + ports: + - protocol: TCP + port: 8080 + targetPort: 8080 \ No newline at end of file diff --git a/deployment/hello-4-ingress-ssl.yaml b/deployment/hello-4-ingress-ssl.yaml new file mode 100644 index 0000000..d37daf4 --- /dev/null +++ b/deployment/hello-4-ingress-ssl.yaml @@ -0,0 +1,19 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: hello-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: test.devtrails.co.nz + http: + paths: + - path: /hello + backend: + serviceName: hello-service + servicePort: 8080 + tls: + - secretName: test-devtrails-crt-secret + hosts: + - test.devtrails.co.nz \ No newline at end of file diff --git a/deployment/hello-4-ingress.yaml b/deployment/hello-4-ingress.yaml new file mode 100644 index 0000000..34312ac --- /dev/null +++ b/deployment/hello-4-ingress.yaml @@ -0,0 +1,14 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: hello-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - http: + paths: + - path: /hello + backend: + serviceName: hello-service + servicePort: 8080 \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 3be9fcd..0000000 --- a/index.js +++ /dev/null @@ -1,14 +0,0 @@ -var express = require('express'); - -// Constants -var DEFAULT_PORT = 8080; -var PORT = process.env.PORT || DEFAULT_PORT; - -// App -var app = express(); -app.get('/', function (req, res) { - res.send('Hello World\n'); -}); - -app.listen(PORT) -console.log('Running on http://localhost:' + PORT); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 85549e5..0000000 --- a/package-lock.json +++ /dev/null @@ -1,362 +0,0 @@ -{ - "name": "docker-debian-hello-world", - "version": "0.0.2", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "2.1.17", - "negotiator": "0.6.1" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.15" - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.1.tgz", - "integrity": "sha512-STB7LZ4N0L+81FJHGla2oboUHTk4PaN1RsOkoRh9OSeEKylvF5hwKYVX1xCLFaCT7MD0BNG/gX2WFMLqY6EMBw==", - "requires": { - "accepts": "1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.2", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "1.3.1", - "type-is": "1.6.15", - "utils-merge": "1.0.1", - "vary": "1.1.2" - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.3.1" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==", - "requires": { - "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.3.1" - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==", - "requires": { - "encodeurl": "1.0.1", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.1" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.17" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - } - } -} diff --git a/package.json b/package.json index 030ab98..a990b06 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,20 @@ "express": "4.16.1" }, "scripts": { - "start": "node ./index.js" + "start": "node ./src/server.js", + "pretest": "eslint /src/*.js && eslint /tests/*.js", + "test": "jest . --ci --testResultsProcessor=jest-junit --coverage" + }, + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-preset-env": "^1.7.0", + "eslint": "^4.9.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-import": "^2.12.0", + "eslint-plugin-jasmine": "^2.10.1", + "jest": "^23.0.1", + "jest-junit": "^4.0.0", + "superagent": "^3.8.3", + "supertest": "^3.1.0" } } diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..009ecb9 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,10 @@ +# must be unique in a given SonarQube instance +sonar.projectKey=docker-debian-hello-world +# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. +sonar.projectName=k8s-hello-world +sonar.projectVersion=1.0 + +# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. +# This property is optional if sonar.modules is set. +sonar.sources=./src +sonar.javascript.lcov.reportPaths=./coverage/lcov.info \ No newline at end of file diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..22380b6 --- /dev/null +++ b/src/app.js @@ -0,0 +1,18 @@ +const express = require('express'); + +const app = express(); + +app.get('/', (req, res) => { + res.send('Hello\n'); +}); + +app.get('/world', (req, res) => { + res.send('Hello world\n'); +}); + +app.get('/vodafone', (req, res) => { + res.send('Hello Vodafone\n'); +}); + +module.exports = app; + diff --git a/src/server.js b/src/server.js new file mode 100644 index 0000000..699ef1b --- /dev/null +++ b/src/server.js @@ -0,0 +1,8 @@ +const app = require('./app.js'); + +const PORT = 8080; + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Running on http://0.0.0.0:${PORT}`); // eslint-disable-line no-console +}); + diff --git a/tests/app.test.js b/tests/app.test.js new file mode 100644 index 0000000..5fc6505 --- /dev/null +++ b/tests/app.test.js @@ -0,0 +1,34 @@ +const request = require('supertest'); +const app = require('./../src/app.js'); + +describe('Test the root path', () => { + test('It should response the GET method', (done) => { + request(app).get('/').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('Hello\n'); + done(); + }); + }); +}); + +describe('Test the world path', () => { + test('It should response the GET method', (done) => { + request(app).get('/world').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('Hello world\n'); + done(); + }); + }); +}); + + +describe('Test the vodafone path', () => { + test('It should response the GET method', (done) => { + request(app).get('/vodafone').then((response) => { + expect(response.statusCode).toBe(200); + expect(response.text).toBe('Hello Vodafone\n'); + done(); + }); + }); +}); +