├── .gitignore ├── README.md ├── SECURITY.md ├── assets └── workshop.jpeg ├── chapter10 ├── deployment │ ├── production │ │ ├── Jenkinsfile │ │ └── docker-compose.yml │ ├── sandbox │ │ ├── Jenkinsfile │ │ └── docker-compose.yml │ └── staging │ │ ├── Jenkinsfile │ │ └── docker-compose.yml ├── discovery │ ├── main.py │ └── requirements.txt ├── pipelines │ ├── movies-loader │ │ └── Jenkinsfile │ ├── movies-marketplace │ │ └── Jenkinsfile │ ├── movies-parser │ │ └── Jenkinsfile │ └── movies-store │ │ └── Jenkinsfile └── swarm │ ├── packer │ └── docker-ce │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ ├── ami.tf │ ├── iam.tf │ ├── s3.tf │ ├── scripts │ └── join-swarm.tpl │ ├── security_groups.tf │ ├── sqs.tf │ ├── swarm_managers.tf │ ├── swarm_workers.tf │ ├── terraform.tf │ ├── variables.tf │ └── vpc.tf ├── chapter11 ├── Jenkinsfile.eks ├── Jenkinsfile.swarm ├── deployment │ ├── helm │ │ └── watchlist │ │ │ ├── .helmignore │ │ │ ├── Chart.yaml │ │ │ ├── templates │ │ │ ├── movies-loader │ │ │ │ ├── configmap.yaml │ │ │ │ └── deployment.yaml │ │ │ ├── movies-marketplace │ │ │ │ ├── deployment.yaml │ │ │ │ └── service.yaml │ │ │ ├── movies-parser │ │ │ │ ├── configmap.yaml │ │ │ │ └── deployment.yaml │ │ │ ├── movies-store │ │ │ │ ├── deployment.yaml │ │ │ │ └── service.yaml │ │ │ ├── namespace.yaml │ │ │ └── secret.yaml │ │ │ └── values.yaml │ ├── kompose │ │ ├── docker-compose.yml │ │ ├── mongodb-deployment.yaml │ │ ├── movies-loader-deployment.yaml │ │ ├── movies-marketplace-deployment.yaml │ │ ├── movies-marketplace-service.yaml │ │ ├── movies-parser-deployment.yaml │ │ ├── movies-store-deployment.yaml │ │ └── movies-store-service.yaml │ └── kubectl │ │ ├── Jenkinsfile │ │ ├── deployments │ │ ├── mongodb-deploy.yaml │ │ ├── movies-loader-deploy.yaml │ │ ├── movies-marketplace-deploy.yaml │ │ ├── movies-parser-deploy.yaml │ │ └── movies-store-deploy.yaml │ │ └── services │ │ ├── mongodb-svc.yaml │ │ ├── movies-marketplace.svc.yaml │ │ └── movies-store.svc.yaml ├── eks │ ├── eks_masters.tf │ ├── eks_workers.tf │ ├── terraform.tf │ ├── variables.tf │ └── vpc.tf ├── jx │ ├── movies-store │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── .helmignore │ │ ├── Dockerfile │ │ ├── Jenkinsfile │ │ ├── Makefile │ │ ├── OWNERS │ │ ├── OWNERS_ALIASES │ │ ├── README.md │ │ ├── charts │ │ │ ├── jx-movies-store │ │ │ │ ├── .helmignore │ │ │ │ ├── Chart.yaml │ │ │ │ ├── Makefile │ │ │ │ ├── README.md │ │ │ │ ├── templates │ │ │ │ │ ├── NOTES.txt │ │ │ │ │ ├── _helpers.tpl │ │ │ │ │ ├── canary.yaml │ │ │ │ │ ├── deployment.yaml │ │ │ │ │ ├── hpa.yaml │ │ │ │ │ ├── ingress.yaml │ │ │ │ │ ├── ksvc.yaml │ │ │ │ │ └── service.yaml │ │ │ │ └── values.yaml │ │ │ └── preview │ │ │ │ ├── Chart.yaml │ │ │ │ ├── Makefile │ │ │ │ ├── requirements.yaml │ │ │ │ └── values.yaml │ │ ├── main.go │ │ ├── main_test.go │ │ ├── movies.json │ │ ├── skaffold.yaml │ │ └── watch.sh │ ├── watchlist-production │ │ ├── .gitignore │ │ ├── .pre-commit-config.yaml │ │ ├── Jenkinsfile │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── env │ │ │ ├── Chart.yaml │ │ │ ├── requirements.yaml │ │ │ ├── templates │ │ │ │ ├── .gitignore │ │ │ │ └── wildcardcert-secret.yaml │ │ │ └── values.yaml │ │ └── jenkins-x.yml │ └── watchlist-staging │ │ ├── .gitignore │ │ ├── .pre-commit-config.yaml │ │ ├── Jenkinsfile │ │ ├── LICENSE │ │ ├── Makefile │ │ ├── README.md │ │ ├── env │ │ ├── Chart.yaml │ │ ├── requirements.yaml │ │ ├── templates │ │ │ ├── .gitignore │ │ │ └── wildcardcert-secret.yaml │ │ └── values.yaml │ │ └── jenkins-x.yml ├── k8s-dashboard │ └── eks-admin.yaml ├── microservices │ ├── Chart.yaml │ ├── movies-loader │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ │ ├── configmap.yaml │ │ │ └── deployment.yaml │ │ └── values.yaml │ ├── movies-marketplace │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ │ ├── deployment.yaml │ │ │ └── service.yaml │ │ └── values.yaml │ ├── movies-parser │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ │ ├── configmap.yaml │ │ │ └── deployment.yaml │ │ └── values.yaml │ └── movies-store │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── deployment.yaml │ │ ├── secrets.yaml │ │ └── service.yaml │ │ └── values.yaml ├── pipeline │ ├── movies-loader │ │ └── Jenkinsfile │ ├── movies-marketplace │ │ └── Jenkinsfile │ ├── movies-parser │ │ └── Jenkinsfile │ └── movies-store │ │ └── Jenkinsfile └── values.overriden.yaml ├── chapter12 ├── functions │ ├── .gitignore │ ├── movies-loader │ │ ├── .gitignore │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── index.py │ │ ├── movies.json │ │ ├── requirements.txt │ │ └── test_index.py │ ├── movies-marketplace │ │ ├── .dockerignore │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── README.md │ │ ├── angular.json │ │ ├── browserslist │ │ ├── e2e │ │ │ ├── protractor.conf.js │ │ │ ├── src │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ └── app.po.ts │ │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── sonar-project.properties │ │ ├── src │ │ │ ├── app │ │ │ │ ├── api.service.spec.ts │ │ │ │ ├── api.service.ts │ │ │ │ ├── app-routing.module.ts │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.spec.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.module.ts │ │ │ │ ├── dashboard │ │ │ │ │ ├── dashboard.component.css │ │ │ │ │ ├── dashboard.component.html │ │ │ │ │ ├── dashboard.component.spec.ts │ │ │ │ │ └── dashboard.component.ts │ │ │ │ ├── favorites │ │ │ │ │ ├── favorites.component.css │ │ │ │ │ ├── favorites.component.html │ │ │ │ │ ├── favorites.component.spec.ts │ │ │ │ │ └── favorites.component.ts │ │ │ │ └── movie │ │ │ │ │ ├── movie.component.css │ │ │ │ │ ├── movie.component.html │ │ │ │ │ ├── movie.component.spec.ts │ │ │ │ │ └── movie.component.ts │ │ │ ├── assets │ │ │ │ ├── .gitkeep │ │ │ │ └── images │ │ │ │ │ └── logo.png │ │ │ ├── environments │ │ │ │ ├── environment.production.ts │ │ │ │ ├── environment.sandbox.ts │ │ │ │ ├── environment.staging.ts │ │ │ │ └── environment.ts │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── main.ts │ │ │ ├── polyfills.ts │ │ │ ├── styles.css │ │ │ └── test.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.spec.json │ │ └── tslint.json │ ├── movies-parser │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Gopkg.lock │ │ ├── Gopkg.toml │ │ ├── Jenkinsfile │ │ ├── main.go │ │ └── main_test.go │ └── movies-store │ │ ├── .dockerignore │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── Jenkinsfile.declarative │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── src │ │ ├── favorites │ │ │ ├── findAll │ │ │ │ └── index.js │ │ │ └── insert │ │ │ │ └── index.js │ │ └── movies │ │ │ ├── findAll │ │ │ └── index.js │ │ │ └── findOne │ │ │ └── index.js │ │ └── test │ │ ├── data.json │ │ ├── favorites.spec.js │ │ └── movies.spec.js └── terraform │ ├── apigateway.tf │ ├── deployment.zip │ ├── dynamodb.tf │ ├── events.tf │ ├── lambda.tf │ ├── modules │ ├── function │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── method │ │ ├── main.tf │ │ └── variables.tf │ ├── outputs.tf │ ├── policies.tf │ ├── s3.tf │ ├── sqs.tf │ ├── terraform.tf │ └── variables.tf ├── chapter13 ├── cloudwatch │ └── dashboard.json ├── elasticsearch │ ├── packer │ │ ├── elasticsearch.yml │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── elasticsearch.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── filebeat │ ├── filebeat.yml │ └── setup.sh ├── grafana │ ├── dashboard │ │ ├── influxdb.json │ │ └── prometheus.json │ ├── packer │ │ ├── grafana.ini │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── grafana.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── influxdb │ ├── packer │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── influxdb.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── kibana │ ├── dashboard │ │ └── jenkins.json │ ├── packer │ │ ├── kibana.yml │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── kibana.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── logstash │ ├── packer │ │ ├── jenkins.conf │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── loadbalancers.tf │ │ ├── logstash.tf │ │ ├── outputs.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf ├── prometheus │ ├── packer │ │ ├── prometheus.service │ │ ├── prometheus.yml │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ │ ├── dns.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── prometheus.tf │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ └── variables.tf └── telegraf │ ├── setup.sh │ └── telegraf.conf ├── chapter14 ├── Dockerfile ├── Jenkinsfile ├── backup.sh ├── docker-compose.yml ├── library │ └── vars │ │ ├── commitAuthor.groovy │ │ ├── commitID.groovy │ │ ├── commitMessage.groovy │ │ └── notifySlack.groovy └── src │ └── com │ └── labouardy │ └── utils │ └── Git.groovy ├── chapter2 ├── Jenkinsfile.declarative └── Jenkinsfile.scripted ├── chapter3 └── Jenkinsfile ├── chapter4 ├── distributed │ ├── master │ │ ├── config │ │ │ ├── install-plugins.sh │ │ │ ├── jenkins │ │ │ └── plugins.txt │ │ ├── scripts │ │ │ ├── basic-security.groovy │ │ │ ├── csrf-protection.groovy │ │ │ ├── disable-cli.groovy │ │ │ ├── disable-jnlp.groovy │ │ │ ├── node-agent.groovy │ │ │ └── skip-jenkins-setup.groovy │ │ ├── setup.sh │ │ └── template.json │ └── worker │ │ ├── setup.sh │ │ └── template.json ├── packer │ └── policy │ │ └── policy.json └── standalone │ ├── setup.sh │ ├── template-multiple-builders.json │ ├── template-with-filter.json │ └── template.json ├── chapter5 ├── bastion.tf ├── cloudwatch.tf ├── jenkins_master.tf ├── jenkins_workers.tf ├── outputs.tf ├── private_rt.tf ├── public_rt.tf ├── route53.tf ├── scripts │ └── join-cluster.tpl ├── subnets.tf ├── terraform.tf ├── variables.tf └── vpc.tf ├── chapter6 ├── azure │ ├── packer │ │ ├── master │ │ │ ├── config │ │ │ │ ├── install-plugins.sh │ │ │ │ ├── jenkins │ │ │ │ └── plugins.txt │ │ │ ├── scripts │ │ │ │ ├── basic-security.groovy │ │ │ │ ├── csrf-protection.groovy │ │ │ │ ├── disable-cli.groovy │ │ │ │ ├── disable-jnlp.groovy │ │ │ │ ├── node-agent.groovy │ │ │ │ └── skip-jenkins-setup.groovy │ │ │ ├── setup.sh │ │ │ └── template.json │ │ └── worker │ │ │ ├── setup.sh │ │ │ └── template.json │ └── terraform │ │ ├── bastion.tf │ │ ├── jenkins_master.tf │ │ ├── jenkins_workers.tf │ │ ├── loadbalancers.tf │ │ ├── outputs.tf │ │ ├── scripts │ │ └── join-cluster.tpl │ │ ├── security_groups.tf │ │ ├── terraform.tf │ │ ├── variables.tf │ │ └── virtual_network.tf ├── digitalocean │ ├── packer │ │ ├── master │ │ │ ├── config │ │ │ │ ├── install-plugins.sh │ │ │ │ ├── jenkins │ │ │ │ └── plugins.txt │ │ │ ├── scripts │ │ │ │ ├── basic-security.groovy │ │ │ │ ├── csrf-protection.groovy │ │ │ │ ├── disable-cli.groovy │ │ │ │ ├── disable-jnlp.groovy │ │ │ │ ├── node-agent.groovy │ │ │ │ └── skip-jenkins-setup.groovy │ │ │ ├── setup.sh │ │ │ └── template.json │ │ └── worker │ │ │ ├── setup.sh │ │ │ └── template.json │ └── terraform │ │ ├── jenkins_master.tf │ │ ├── jenkins_workers.tf │ │ ├── outputs.tf │ │ ├── scripts │ │ └── join-cluster.tpl │ │ ├── terraform.tf │ │ └── variables.tf └── gcp │ ├── packer │ ├── master │ │ ├── config │ │ │ ├── install-plugins.sh │ │ │ ├── jenkins │ │ │ └── plugins.txt │ │ ├── scripts │ │ │ ├── basic-security.groovy │ │ │ ├── csrf-protection.groovy │ │ │ ├── disable-cli.groovy │ │ │ ├── disable-jnlp.groovy │ │ │ ├── node-agent.groovy │ │ │ └── skip-jenkins-setup.groovy │ │ ├── setup.sh │ │ └── template.json │ └── worker │ │ ├── setup.sh │ │ └── template.json │ └── terraform │ ├── bastion.tf │ ├── jenkins_master.tf │ ├── jenkins_workers.tf │ ├── network.tf │ ├── outputs.tf │ ├── scripts │ └── join-cluster.tpl │ ├── terraform.tf │ └── variables.tf ├── chapter7 ├── README.md ├── jobs │ ├── movies-loader.xml │ ├── movies-marketplace.xml │ ├── movies-parser.xml │ └── movies-store.xml ├── microservices │ ├── movies-loader │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── main.py │ │ ├── movies.json │ │ ├── requirements.txt │ │ └── test_main.py │ ├── movies-marketplace │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── README.md │ │ ├── angular.json │ │ ├── browserslist │ │ ├── e2e │ │ │ ├── protractor.conf.js │ │ │ ├── src │ │ │ │ ├── app.e2e-spec.ts │ │ │ │ └── app.po.ts │ │ │ └── tsconfig.json │ │ ├── karma.conf.js │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── sonar-project.properties │ │ ├── src │ │ │ ├── app │ │ │ │ ├── api.service.spec.ts │ │ │ │ ├── api.service.ts │ │ │ │ ├── app-routing.module.ts │ │ │ │ ├── app.component.css │ │ │ │ ├── app.component.html │ │ │ │ ├── app.component.spec.ts │ │ │ │ ├── app.component.ts │ │ │ │ ├── app.module.ts │ │ │ │ ├── dashboard │ │ │ │ │ ├── dashboard.component.css │ │ │ │ │ ├── dashboard.component.html │ │ │ │ │ ├── dashboard.component.spec.ts │ │ │ │ │ └── dashboard.component.ts │ │ │ │ ├── favorites │ │ │ │ │ ├── favorites.component.css │ │ │ │ │ ├── favorites.component.html │ │ │ │ │ ├── favorites.component.spec.ts │ │ │ │ │ └── favorites.component.ts │ │ │ │ └── movie │ │ │ │ │ ├── movie.component.css │ │ │ │ │ ├── movie.component.html │ │ │ │ │ ├── movie.component.spec.ts │ │ │ │ │ └── movie.component.ts │ │ │ ├── assets │ │ │ │ ├── .gitkeep │ │ │ │ └── images │ │ │ │ │ └── logo.png │ │ │ ├── environments │ │ │ │ ├── environment.production.ts │ │ │ │ ├── environment.sandbox.ts │ │ │ │ ├── environment.staging.ts │ │ │ │ └── environment.ts │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── main.ts │ │ │ ├── polyfills.ts │ │ │ ├── styles.css │ │ │ └── test.ts │ │ ├── tsconfig.app.json │ │ ├── tsconfig.json │ │ ├── tsconfig.spec.json │ │ └── tslint.json │ ├── movies-parser │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Gopkg.lock │ │ ├── Gopkg.toml │ │ ├── Jenkinsfile │ │ ├── c.out │ │ ├── go.mod │ │ ├── main.go │ │ └── main_test.go │ └── movies-store │ │ ├── .dockerignore │ │ ├── .eslintrc.json │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.test │ │ ├── Jenkinsfile │ │ ├── README.md │ │ ├── dao copy.js │ │ ├── dao.js │ │ ├── index.js │ │ ├── package-lock.json │ │ ├── package.json │ │ └── test │ │ ├── dao.spec.js │ │ └── movies.json └── webhook-forwarder │ ├── index.js │ ├── package.json │ └── terraform │ ├── apigateway.tf │ ├── lambda.tf │ ├── outputs.tf │ ├── terraform.tf │ └── variables.tf ├── chapter8 ├── declarative │ ├── Jenkinsfile.docker │ └── Jenkinsfile.node ├── pipelines │ ├── movies-loader │ │ └── Jenkinsfile │ ├── movies-marketplace │ │ └── Jenkinsfile │ ├── movies-parser │ │ └── Jenkinsfile │ └── movies-store │ │ └── Jenkinsfile ├── services │ ├── dns.tf │ ├── loadbalancers.tf │ ├── outputs.tf │ ├── security_groups.tf │ ├── terraform.tf │ └── variables.tf ├── sonar-scanner │ ├── setup.sh │ └── template.json └── sonarqube │ ├── packer │ ├── setup.sh │ ├── sonar.init.d │ └── template.json │ └── terraform │ ├── dns.tf │ ├── loadbalancers.tf │ ├── outputs.tf │ ├── sonarqube.tf │ ├── terraform.tf │ └── variables.tf └── chapter9 ├── Jenkinsfile.declarative ├── anchore └── docker-compose.yml ├── nexus ├── packer │ ├── nexus.rc │ ├── repository.json │ ├── setup.sh │ └── template.json └── terraform │ ├── dns.tf │ ├── loadbalancers.tf │ ├── nexus.tf │ ├── outputs.tf │ ├── security_groups.tf │ ├── terraform.tf │ └── variables.tf └── pipelines ├── movies-loader ├── Dockerfile └── Jenkinsfile ├── movies-marketplace ├── Dockerfile └── Jenkinsfile ├── movies-parser ├── Dockerfile └── Jenkinsfile └── movies-store ├── Dockerfile └── Jenkinsfile /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | *.tfstate 3 | *.tfstate.backup 4 | *.lock.info 5 | variables.tfvars 6 | *.lock.info 7 | values.override.yaml 8 | # python 9 | test_main.pyc 10 | .nyc_output 11 | # compiled output 12 | dist 13 | tmp 14 | out-tsc 15 | # Only exists if Bazel was run 16 | bazel-out 17 | 18 | # dependencies 19 | node_modules 20 | 21 | # profiling files 22 | chrome-profiler-events*.json 23 | speed-measure-plugin*.json 24 | 25 | # IDEs and editors 26 | .idea 27 | .project 28 | .classpath 29 | .c9/ 30 | *.launch 31 | .settings/ 32 | *.sublime-workspace 33 | 34 | # IDE - VSCode 35 | .vscode/* 36 | !.vscode/settings.json 37 | !.vscode/tasks.json 38 | !.vscode/launch.json 39 | !.vscode/extensions.json 40 | .history/* 41 | 42 | # misc 43 | .sass-cache 44 | connect.lock 45 | coverage 46 | libpeerconnection.log 47 | npm-debug.log 48 | yarn-error.log 49 | testem.log 50 | typings 51 | 52 | # System Files 53 | .DS_Store 54 | Thumbs.db 55 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ------- | ------------------ | 7 | | 1.0.0 | :white_check_mark: | 8 | 9 | -------------------------------------------------------------------------------- /assets/workshop.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/assets/workshop.jpeg -------------------------------------------------------------------------------- /chapter10/deployment/production/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def swarmManager = 'manager.production.domain.com' 2 | def region = 'ECR REGION' 3 | 4 | node('master'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | sshagent (credentials: ['swarm-production']){ 10 | stage('Copy'){ 11 | sh "scp -o StrictHostKeyChecking=no docker-compose.yml ec2-user@${swarmManager}:/home/ec2-user" 12 | } 13 | 14 | stage('Deploy stack'){ 15 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} '\$(\$(aws ecr get-login --no-include-email --region ${region}))' || true" 16 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} docker stack deploy --compose-file docker-compose.yml --with-registry-auth watchlist" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter10/deployment/sandbox/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def swarmManager = 'manager.sandbox.domain.com' 2 | def region = 'ECR REGION' 3 | 4 | node('master'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | sshagent (credentials: ['swarm-sandbox']){ 10 | stage('Copy'){ 11 | sh "scp -o StrictHostKeyChecking=no docker-compose.yml ec2-user@${swarmManager}:/home/ec2-user" 12 | } 13 | 14 | stage('Deploy stack'){ 15 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} '\$(\$(aws ecr get-login --no-include-email --region ${region}))' || true" 16 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} docker stack deploy --compose-file docker-compose.yml --with-registry-auth watchlist" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter10/deployment/staging/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def swarmManager = 'manager.staging.domain.com' 2 | def region = 'ECR REGION' 3 | 4 | node('master'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | sshagent (credentials: ['swarm-staging']){ 10 | stage('Copy'){ 11 | sh "scp -o StrictHostKeyChecking=no docker-compose.yml ec2-user@${swarmManager}:/home/ec2-user" 12 | } 13 | 14 | stage('Deploy stack'){ 15 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} '\$(\$(aws ecr get-login --no-include-email --region ${region}))' || true" 16 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} docker stack deploy --compose-file docker-compose.yml --with-registry-auth watchlist" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chapter10/discovery/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | docker -------------------------------------------------------------------------------- /chapter10/swarm/packer/docker-ce/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Docker engine" 4 | yum update -y 5 | yum install docker -y 6 | usermod -aG docker ec2-user 7 | systemctl enable docker -------------------------------------------------------------------------------- /chapter10/swarm/packer/docker-ce/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "AWS REGION", 4 | "aws_profile": "AWS PROFILE", 5 | "source_ami" : "AMAZON LINUX AMI 2 ID", 6 | "instance_type": "INSTANCE TYPE" 7 | }, 8 | "builders" : [ 9 | { 10 | "type" : "amazon-ebs", 11 | "profile" : "{{user `aws_profile`}}", 12 | "region" : "{{user `region`}}", 13 | "instance_type" : "{{user `instance_type`}}", 14 | "source_ami" : "{{user `source_ami`}}", 15 | "ssh_username" : "ec2-user", 16 | "ami_name" : "docker-18.09.9-ce", 17 | "ami_description" : "Docker engine AMI", 18 | "run_tags" : { 19 | "Name" : "packer-builder" 20 | } 21 | } 22 | ], 23 | "provisioners" : [ 24 | { 25 | "type" : "shell", 26 | "script" : "./setup.sh", 27 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /chapter10/swarm/terraform/ami.tf: -------------------------------------------------------------------------------- 1 | // Find latest Docker AMI 2 | data "aws_ami" "docker" { 3 | most_recent = true 4 | owners = ["self"] 5 | 6 | filter { 7 | name = "name" 8 | values = ["docker-*"] 9 | } 10 | } -------------------------------------------------------------------------------- /chapter10/swarm/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_instance_profile" "swarm_profile" { 2 | name = "swarm-profile-${var.environment}" 3 | role = aws_iam_role.swarm_role.name 4 | } 5 | 6 | resource "aws_iam_role" "swarm_role" { 7 | name = "swarm-role-${var.environment}" 8 | path = "/" 9 | 10 | assume_role_policy = < .git/commitID' 24 | def commitID = readFile('.git/commitID').trim() 25 | sh 'rm .git/commitID' 26 | commitID 27 | } 28 | -------------------------------------------------------------------------------- /chapter11/Jenkinsfile.swarm: -------------------------------------------------------------------------------- 1 | def swarmManager = 'manager.sandbox.slowcoder.com' 2 | def region = 'eu-west-3' 3 | 4 | node('master'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | sshagent (credentials: ['swarm-sandbox']){ 10 | stage('Copy'){ 11 | sh "scp -o StrictHostKeyChecking=no docker-compose.yml ec2-user@${swarmManager}:/home/ec2-user" 12 | } 13 | 14 | stage('Deploy stack'){ 15 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} '\$(\$(aws ecr get-login --no-include-email --region ${region}))' || true" 16 | sh "ssh -oStrictHostKeyChecking=no ec2-user@${swarmManager} docker stack deploy --compose-file docker-compose.yml --with-registry-auth watchlist" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: watchlist 3 | description: Top 100 iMDB best movies in history 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 7 | maintainers: 8 | - name: Mohamed Labouardy 9 | email: mohamed@labouardy.com 10 | dependencies: 11 | - name: mongodb 12 | version: 7.8.10 13 | repository: https://charts.bitnami.com/bitnami 14 | alias: mongodb -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/movies-loader/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.namespace }}-movies-loader 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: {{ .Values.namespace }}-movies-loader 8 | data: 9 | AWS_REGION: {{ .Values.services.aws.region }} 10 | SQS_URL: https://sqs.{{ .Values.services.aws.region }}.amazonaws.com/{{ .Values.services.aws.account }}/movies_to_parse_{{ .Values.environment }} -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/movies-marketplace/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-marketplace 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: movies-marketplace 8 | tier: frontend 9 | spec: 10 | selector: 11 | matchLabels: 12 | app: movies-marketplace 13 | template: 14 | metadata: 15 | name: movies-marketplace 16 | labels: 17 | app: movies-marketplace 18 | tier: frontend 19 | annotations: 20 | jenkins/build: {{ .Values.metadata.jenkins.buildTag | quote }} 21 | git/commitId: {{ .Values.metadata.git.commitId | quote }} 22 | spec: 23 | containers: 24 | - name: movies-marketplace 25 | image: "{{ .Values.services.registry.uri }}/mlabouardy/movies-marketplace:{{ .Values.deployment.tag }}" 26 | imagePullPolicy: Always 27 | {{- if .Values.services.registry.secret }} 28 | imagePullSecrets: 29 | - name: {{ .Values.services.registry.secret }} 30 | {{- end }} -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/movies-marketplace/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-marketplace 5 | namespace: {{ .Values.namespace }} 6 | spec: 7 | ports: 8 | - name: http 9 | port: 80 10 | targetPort: 80 11 | type: LoadBalancer 12 | selector: 13 | app: movies-marketplace -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/movies-parser/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.namespace }}-movies-parser 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: {{ .Values.namespace }}-movies-parser 8 | data: 9 | AWS_REGION: {{ .Values.services.aws.region }} 10 | SQS_URL: https://sqs.{{ .Values.services.aws.region }}.amazonaws.com/{{ .Values.services.aws.account }}/movies_to_parse_{{ .Values.environment }} -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/movies-store/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-store 5 | namespace: {{ .Values.namespace }} 6 | annotations: 7 | service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http 8 | {{- if .Values.deployment.https.ssl }} 9 | service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{{ .Values.services.aws.region }}:{{ .Values.services.aws.account }}:certificate/{{ .Values.deployment.https.ssl }} 10 | service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" 11 | {{- end }} 12 | spec: 13 | ports: 14 | - name: http 15 | port: 80 16 | targetPort: 3000 17 | {{- if .Values.deployment.https.ssl }} 18 | - name: https 19 | port: 443 20 | targetPort: 3000 21 | {{- end }} 22 | type: LoadBalancer 23 | selector: 24 | app: movies-store -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/namespace.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | name: {{ .Values.namespace }} -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ .Values.namespace }}-secrets 5 | namespace: {{ .Values.namespace }} 6 | data: 7 | MONGO_URI: {{ .Values.services.mongodb.uri | b64enc }} 8 | MONGO_DATABASE : {{ .Values.mongodb.mongodbDatabase | b64enc }} 9 | MONGODB_USERNAME : {{ .Values.mongodb.mongodbUsername | b64enc }} 10 | MONGODB_PASSWORD : {{ .Values.mongodb.mongodbPassword | b64enc }} -------------------------------------------------------------------------------- /chapter11/deployment/helm/watchlist/values.yaml: -------------------------------------------------------------------------------- 1 | namespace: 'watchlist' 2 | environment: '' 3 | 4 | metadata: 5 | git: 6 | commitId: none 7 | jenkins: 8 | buildTag: none 9 | 10 | mongodb: 11 | fullnameOverride: 'mongodb' 12 | mongodbRootPassword: '' 13 | mongodbUsername: '' 14 | mongodbDatabase: 'watchlist' 15 | 16 | services: 17 | aws: 18 | account: '' 19 | region: '' 20 | mongodb: 21 | uri: '' 22 | registry: 23 | uri: '' 24 | secret: '' 25 | 26 | deployment: 27 | tag: '' 28 | workers: 29 | replicas: 2 30 | https: 31 | ssl: '' -------------------------------------------------------------------------------- /chapter11/deployment/kompose/movies-marketplace-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose convert -f docker-compose.yml 6 | kompose.version: 1.21.0 (992df58d8) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: movies-marketplace 10 | name: movies-marketplace 11 | spec: 12 | ports: 13 | - name: "80" 14 | port: 80 15 | targetPort: 80 16 | selector: 17 | io.kompose.service: movies-marketplace 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /chapter11/deployment/kompose/movies-store-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | annotations: 5 | kompose.cmd: kompose convert -f docker-compose.yml 6 | kompose.version: 1.21.0 (992df58d8) 7 | creationTimestamp: null 8 | labels: 9 | io.kompose.service: movies-store 10 | name: movies-store 11 | spec: 12 | ports: 13 | - name: "3000" 14 | port: 3000 15 | targetPort: 3000 16 | selector: 17 | io.kompose.service: movies-store 18 | status: 19 | loadBalancer: {} 20 | -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def region = 'eu-west-3' 2 | def accounts = [master:'production', preprod:'staging', develop:'sandbox'] 3 | 4 | node('master'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | stage('Authentication'){ 10 | sh "aws eks update-kubeconfig --name ${accounts[env.BRANCH_NAME]} --region ${region}" 11 | } 12 | 13 | stage('Deploy'){ 14 | sh 'kubectl apply -f deployments/' 15 | sh 'kubectl apply -f services/' 16 | } 17 | } -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/deployments/mongodb-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mongodb 5 | namespace: watchlist 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: mongodb 10 | tier: mongodb 11 | template: 12 | metadata: 13 | labels: 14 | app: mongodb 15 | tier: mongodb 16 | spec: 17 | containers: 18 | - name: mongodb 19 | image: bitnami/mongodb:latest 20 | ports: 21 | - containerPort: 27017 22 | name: mongodb 23 | env: 24 | - name: MONGODB_USERNAME 25 | valueFrom: 26 | secretKeyRef: 27 | name: mongodb-access 28 | key: username 29 | - name: MONGODB_PASSWORD 30 | valueFrom: 31 | secretKeyRef: 32 | name: mongodb-access 33 | key: password 34 | - name: MONGODB_DATABASE 35 | valueFrom: 36 | secretKeyRef: 37 | name: mongodb-access 38 | key: database -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/deployments/movies-loader-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-loader 5 | namespace: watchlist 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: movies-loader 10 | template: 11 | metadata: 12 | labels: 13 | app: movies-loader 14 | spec: 15 | containers: 16 | - name: movies-loader 17 | image: ID.dkr.ecr.REGION.amazonaws.com/USER/movies-loader:develop 18 | env: 19 | - name: AWS_REGION 20 | value: REGION 21 | - name: SQS_URL 22 | value: https://sqs.REGION.amazonaws.com/USER/movies_to_parse_sandbox -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/deployments/movies-marketplace-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-marketplace 5 | namespace: watchlist 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: movies-marketplace 10 | template: 11 | metadata: 12 | labels: 13 | app: movies-marketplace 14 | spec: 15 | containers: 16 | - name: movies-marketplace 17 | image: ID.dkr.ecr.REGION.amazonaws.com/USER/movies-marketplace:develop 18 | ports: 19 | - containerPort: 80 -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/deployments/movies-parser-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-parser 5 | namespace: watchlist 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: movies-parser 10 | template: 11 | metadata: 12 | labels: 13 | app: movies-parser 14 | spec: 15 | containers: 16 | - name: movies-parser 17 | image: ID.dkr.ecr.REGION.amazonaws.com/USER/movies-parser:develop 18 | env: 19 | - name: AWS_REGION 20 | value: REGION 21 | - name: SQS_URL 22 | value: https://sqs.REGION.amazonaws.com/ID/movies_to_parse_sandbox 23 | - name: MONGO_DATABASE 24 | valueFrom: 25 | secretKeyRef: 26 | name: mongodb-access 27 | key: database 28 | - name: MONGO_URI 29 | valueFrom: 30 | secretKeyRef: 31 | name: mongodb-access 32 | key: uri -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/deployments/movies-store-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-store 5 | namespace: watchlist 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: movies-store 10 | template: 11 | metadata: 12 | labels: 13 | app: movies-store 14 | spec: 15 | containers: 16 | - name: movies-store 17 | image: ID.dkr.ecr.REGION.amazonaws.com/USER/movies-store:develop 18 | ports: 19 | - containerPort: 3000 20 | env: 21 | - name: MONGO_URI 22 | valueFrom: 23 | secretKeyRef: 24 | name: mongodb-access 25 | key: uri -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/services/mongodb-svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: mongodb 5 | namespace: watchlist 6 | spec: 7 | ports: 8 | - port: 27017 9 | selector: 10 | app: mongodb 11 | tier: mongodb 12 | clusterIP: None -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/services/movies-marketplace.svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-marketplace 5 | namespace: watchlist 6 | spec: 7 | ports: 8 | - port: 80 9 | targetPort: 80 10 | selector: 11 | app: movies-marketplace 12 | type: LoadBalancer -------------------------------------------------------------------------------- /chapter11/deployment/kubectl/services/movies-store.svc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-store 5 | namespace: watchlist 6 | annotations: 7 | service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http 8 | service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{region}:{user id}:certificate/{id} 9 | service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" 10 | spec: 11 | ports: 12 | - name: http 13 | port: 80 14 | targetPort: 3000 15 | - name: https 16 | port: 443 17 | targetPort: 3000 18 | selector: 19 | app: movies-store 20 | type: LoadBalancer -------------------------------------------------------------------------------- /chapter11/eks/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter11/jx/movies-store/.dockerignore: -------------------------------------------------------------------------------- 1 | draft.toml 2 | target/classes 3 | target/generated-sources 4 | target/generated-test-sources 5 | target/maven-archiver 6 | target/maven-status 7 | target/surefire-reports 8 | target/test-classes 9 | target/*.original 10 | charts/ 11 | NOTICE 12 | LICENSE 13 | README.md -------------------------------------------------------------------------------- /chapter11/jx/movies-store/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .project 3 | .classpath 4 | .idea 5 | .cache 6 | .DS_Store 7 | *.im? 8 | target 9 | work 10 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | *.png 23 | 24 | # known compile time folders 25 | target/ 26 | node_modules/ 27 | vendor/ -------------------------------------------------------------------------------- /chapter11/jx/movies-store/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | EXPOSE 8080 3 | ENTRYPOINT ["/jx-movies-store"] 4 | COPY ./bin/ / -------------------------------------------------------------------------------- /chapter11/jx/movies-store/OWNERS: -------------------------------------------------------------------------------- 1 | approvers: 2 | - mlabouardy 3 | reviewers: 4 | - mlabouardy 5 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/OWNERS_ALIASES: -------------------------------------------------------------------------------- 1 | aliases: 2 | - mlabouardy 3 | best-approvers: 4 | - mlabouardy 5 | best-reviewers: 6 | - mlabouardy 7 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/README.md: -------------------------------------------------------------------------------- 1 | # Jenkins X Pipeline 2 | 3 | # Author 4 | 5 | Mohamed Labouardy 6 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Kubernetes 3 | icon: https://raw.githubusercontent.com/jenkins-x/jenkins-x-platform/d273e09/images/go.png 4 | name: jx-movies-store 5 | version: 0.1.0-SNAPSHOT 6 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/README.md: -------------------------------------------------------------------------------- 1 | # golang application -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 2 | Get the application URL by running these commands: 3 | 4 | kubectl get ingress {{ template "fullname" . }} 5 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | */}} 13 | {{- define "fullname" -}} 14 | {{- $name := default .Chart.Name .Values.nameOverride -}} 15 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 16 | {{- end -}} 17 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.hpa.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ template "fullname" . }} 6 | labels: 7 | draft: {{ default "draft-app" .Values.draft }} 8 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 9 | spec: 10 | scaleTargetRef: 11 | apiVersion: apps/v1 12 | kind: Deployment 13 | name: {{ template "fullname" . }} 14 | minReplicas: {{ .Values.hpa.minReplicas }} 15 | maxReplicas: {{ .Values.hpa.maxReplicas }} 16 | metrics: 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.hpa.cpuTargetAverageUtilization }} 21 | - type: Resource 22 | resource: 23 | name: memory 24 | targetAverageUtilization: {{ .Values.hpa.memoryTargetAverageUtilization }} 25 | {{- end }} -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/jx-movies-store/templates/service.yaml: -------------------------------------------------------------------------------- 1 | {{- if or .Values.knativeDeploy .Values.canary.enabled }} 2 | {{- else }} 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | {{- if .Values.service.name }} 7 | name: {{ .Values.service.name }} 8 | {{- else }} 9 | name: {{ template "fullname" . }} 10 | {{- end }} 11 | labels: 12 | chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" 13 | {{- if .Values.service.annotations }} 14 | annotations: 15 | {{ toYaml .Values.service.annotations | indent 4 }} 16 | {{- end }} 17 | spec: 18 | type: {{ .Values.service.type }} 19 | ports: 20 | - port: {{ .Values.service.externalPort }} 21 | targetPort: {{ .Values.service.internalPort }} 22 | protocol: TCP 23 | name: http 24 | selector: 25 | app: {{ template "fullname" . }} 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/preview/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | description: A Helm chart for Kubernetes 3 | icon: https://raw.githubusercontent.com/jenkins-x/jenkins-x-platform/master/images/go.png 4 | name: preview 5 | version: 0.1.0-SNAPSHOT 6 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/preview/Makefile: -------------------------------------------------------------------------------- 1 | OS := $(shell uname) 2 | 3 | preview: 4 | ifeq ($(OS),Darwin) 5 | sed -i "" -e "s/version:.*/version: $(PREVIEW_VERSION)/" Chart.yaml 6 | sed -i "" -e "s/version:.*/version: $(PREVIEW_VERSION)/" ../*/Chart.yaml 7 | sed -i "" -e "s/tag:.*/tag: $(PREVIEW_VERSION)/" values.yaml 8 | else ifeq ($(OS),Linux) 9 | sed -i -e "s/version:.*/version: $(PREVIEW_VERSION)/" Chart.yaml 10 | sed -i -e "s/version:.*/version: $(PREVIEW_VERSION)/" ../*/Chart.yaml 11 | sed -i -e "s|repository:.*|repository: $(DOCKER_REGISTRY)\/crew-sandbox\/jx-movies-store|" values.yaml 12 | sed -i -e "s/tag:.*/tag: $(PREVIEW_VERSION)/" values.yaml 13 | else 14 | echo "platfrom $(OS) not supported to release from" 15 | exit -1 16 | endif 17 | echo " version: $(PREVIEW_VERSION)" >> requirements.yaml 18 | jx step helm build 19 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/preview/requirements.yaml: -------------------------------------------------------------------------------- 1 | # !! File must end with empty line !! 2 | dependencies: 3 | - alias: expose 4 | name: exposecontroller 5 | repository: http://chartmuseum.jenkins-x.io 6 | version: 2.3.92 7 | - alias: cleanup 8 | name: exposecontroller 9 | repository: http://chartmuseum.jenkins-x.io 10 | version: 2.3.92 11 | 12 | # !! "alias: preview" must be last entry in dependencies array !! 13 | # !! Place custom dependencies above !! 14 | - alias: preview 15 | name: jx-movies-store 16 | repository: file://../jx-movies-store 17 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/charts/preview/values.yaml: -------------------------------------------------------------------------------- 1 | 2 | expose: 3 | Annotations: 4 | helm.sh/hook: post-install,post-upgrade 5 | helm.sh/hook-delete-policy: hook-succeeded 6 | config: 7 | exposer: Ingress 8 | http: true 9 | tlsacme: false 10 | 11 | cleanup: 12 | Args: 13 | - --cleanup 14 | Annotations: 15 | helm.sh/hook: pre-delete 16 | helm.sh/hook-delete-policy: hook-succeeded 17 | 18 | preview: 19 | image: 20 | repository: 21 | tag: 22 | pullPolicy: IfNotPresent -------------------------------------------------------------------------------- /chapter11/jx/movies-store/skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v1beta2 2 | kind: Config 3 | build: 4 | artifacts: 5 | - image: crew-sandbox/jx-movies-store 6 | context: . 7 | docker: {} 8 | tagPolicy: 9 | envTemplate: 10 | template: '{{.DOCKER_REGISTRY}}/{{.IMAGE_NAME}}:{{.VERSION}}' 11 | local: {} 12 | deploy: 13 | kubectl: {} 14 | profiles: 15 | - name: dev 16 | build: 17 | tagPolicy: 18 | envTemplate: 19 | template: '{{.DOCKER_REGISTRY}}/{{.IMAGE_NAME}}:{{.DIGEST_HEX}}' 20 | local: {} 21 | deploy: 22 | helm: 23 | releases: 24 | - name: jx-movies-store 25 | chartPath: charts/jx-movies-store 26 | setValueTemplates: 27 | image.repository: '{{.DOCKER_REGISTRY}}/{{.IMAGE_NAME}}' 28 | image.tag: '{{.DIGEST_HEX}}' 29 | -------------------------------------------------------------------------------- /chapter11/jx/movies-store/watch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # watch the go files and continously deploy the service 4 | make linux 5 | skaffold run -p dev 6 | reflex -r "\.go$" -- bash -c 'make linux && skaffold run -p dev' 7 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git@github.com:Yelp/detect-secrets 3 | rev: v0.12.4 4 | hooks: 5 | - id: detect-secrets 6 | args: ['--baseline', '.secrets.baseline'] 7 | exclude: .*/tests/.* -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | options { 3 | disableConcurrentBuilds() 4 | } 5 | agent { 6 | label "jenkins-maven" 7 | } 8 | environment { 9 | DEPLOY_NAMESPACE = "jx-production" 10 | } 11 | stages { 12 | stage('Validate Environment') { 13 | steps { 14 | container('maven') { 15 | dir('env') { 16 | sh 'jx step helm build' 17 | } 18 | } 19 | } 20 | } 21 | stage('Update Environment') { 22 | when { 23 | branch 'master' 24 | } 25 | steps { 26 | container('maven') { 27 | dir('env') { 28 | sh 'jx step helm apply' 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/Makefile: -------------------------------------------------------------------------------- 1 | CHART_REPO := http://jenkins-x-chartmuseum:8080 2 | DIR := "env" 3 | NAMESPACE := "jx-production" 4 | OS := $(shell uname) 5 | 6 | build: clean 7 | rm -rf requirements.lock 8 | helm version 9 | helm init 10 | helm repo add releases ${CHART_REPO} 11 | helm repo add jenkins-x http://chartmuseum.jenkins-x.io 12 | helm dependency build ${DIR} 13 | helm lint ${DIR} 14 | 15 | install: 16 | helm upgrade ${NAMESPACE} ${DIR} --install --namespace ${NAMESPACE} --debug 17 | 18 | delete: 19 | helm delete --purge ${NAMESPACE} --namespace ${NAMESPACE} 20 | 21 | clean: 22 | 23 | 24 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/README.md: -------------------------------------------------------------------------------- 1 | # default-environment-charts 2 | The default git repository used when creating new GitOps based Environments 3 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/env/Chart.yaml: -------------------------------------------------------------------------------- 1 | description: GitOps Environment for this Environment 2 | icon: https://www.cloudbees.com/sites/default/files/Jenkins_8.png 3 | maintainers: 4 | - name: Team 5 | name: env 6 | version: 0.0.1 7 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/env/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - alias: expose 3 | name: exposecontroller 4 | repository: http://chartmuseum.jenkins-x.io 5 | version: 2.3.118 6 | - alias: cleanup 7 | name: exposecontroller 8 | repository: http://chartmuseum.jenkins-x.io 9 | version: 2.3.118 10 | - name: jx-movies-store 11 | repository: http://jenkins-x-chartmuseum:8080 12 | version: 0.0.3 13 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/env/templates/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/env/templates/wildcardcert-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.expose }} 2 | {{- if .Values.expose.config }} 3 | {{- if .Values.expose.config.tlsacme }} 4 | {{- if eq .Values.expose.config.tlsacme "true" }} 5 | apiVersion: v1 6 | data: 7 | tls.crt: Zm9vIC1uCg== 8 | tls.key: Zm9vIC1uCg== 9 | kind: Secret 10 | metadata: 11 | annotations: 12 | {{- if .Values.expose.production }} 13 | replicator.v1.mittwald.de/replicate-from: jx/tls-{{ .Values.expose.config.domain | replace "." "-" }}-p 14 | {{- else }} 15 | replicator.v1.mittwald.de/replicate-from: jx/tls-{{ .Values.expose.config.domain | replace "." "-" }}-s 16 | {{- end }} 17 | {{- if .Values.expose.production }} 18 | name: "tls-{{ .Values.expose.config.domain | replace "." "-" }}-p" 19 | {{- else }} 20 | name: "tls-{{ .Values.expose.config.domain | replace "." "-" }}-s" 21 | {{- end }} 22 | type: kubernetes.io/tls 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/env/values.yaml: -------------------------------------------------------------------------------- 1 | PipelineSecrets: {} 2 | cleanup: 3 | Annotations: 4 | helm.sh/hook: pre-delete 5 | helm.sh/hook-delete-policy: hook-succeeded 6 | Args: 7 | - --cleanup 8 | expose: 9 | Annotations: 10 | helm.sh/hook: post-install,post-upgrade 11 | helm.sh/hook-delete-policy: hook-succeeded 12 | Args: 13 | - --v 14 | - 4 15 | config: 16 | domain: 35.198.184.208.nip.io 17 | exposer: Ingress 18 | http: "true" 19 | jenkins: 20 | Servers: 21 | Global: 22 | EnvVars: 23 | TILLER_NAMESPACE: kube-system 24 | prow: {} 25 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-production/jenkins-x.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - name: DEPLOY_NAMESPACE 3 | value: jx-production 4 | pipelineConfig: 5 | env: 6 | - name: DEPLOY_NAMESPACE 7 | value: jx-production 8 | pipelines: {} 9 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: git@github.com:Yelp/detect-secrets 3 | rev: v0.12.4 4 | hooks: 5 | - id: detect-secrets 6 | args: ['--baseline', '.secrets.baseline'] 7 | exclude: .*/tests/.* -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | options { 3 | disableConcurrentBuilds() 4 | } 5 | agent { 6 | label "jenkins-maven" 7 | } 8 | environment { 9 | DEPLOY_NAMESPACE = "jx-staging" 10 | } 11 | stages { 12 | stage('Validate Environment') { 13 | steps { 14 | container('maven') { 15 | dir('env') { 16 | sh 'jx step helm build' 17 | } 18 | } 19 | } 20 | } 21 | stage('Update Environment') { 22 | when { 23 | branch 'master' 24 | } 25 | steps { 26 | container('maven') { 27 | dir('env') { 28 | sh 'jx step helm apply' 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/Makefile: -------------------------------------------------------------------------------- 1 | CHART_REPO := http://jenkins-x-chartmuseum:8080 2 | DIR := "env" 3 | NAMESPACE := "jx-staging" 4 | OS := $(shell uname) 5 | 6 | build: clean 7 | rm -rf requirements.lock 8 | helm version 9 | helm init 10 | helm repo add releases ${CHART_REPO} 11 | helm repo add jenkins-x http://chartmuseum.jenkins-x.io 12 | helm dependency build ${DIR} 13 | helm lint ${DIR} 14 | 15 | install: 16 | helm upgrade ${NAMESPACE} ${DIR} --install --namespace ${NAMESPACE} --debug 17 | 18 | delete: 19 | helm delete --purge ${NAMESPACE} --namespace ${NAMESPACE} 20 | 21 | clean: 22 | 23 | 24 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/README.md: -------------------------------------------------------------------------------- 1 | # default-environment-charts 2 | The default git repository used when creating new GitOps based Environments 3 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/env/Chart.yaml: -------------------------------------------------------------------------------- 1 | description: GitOps Environment for this Environment 2 | icon: https://www.cloudbees.com/sites/default/files/Jenkins_8.png 3 | maintainers: 4 | - name: Team 5 | name: env 6 | version: 0.0.1 7 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/env/requirements.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - alias: expose 3 | name: exposecontroller 4 | repository: http://chartmuseum.jenkins-x.io 5 | version: 2.3.118 6 | - alias: cleanup 7 | name: exposecontroller 8 | repository: http://chartmuseum.jenkins-x.io 9 | version: 2.3.118 10 | - name: jx-movies-store 11 | repository: http://jenkins-x-chartmuseum:8080 12 | version: 0.0.3 13 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/env/templates/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/env/templates/wildcardcert-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.expose }} 2 | {{- if .Values.expose.config }} 3 | {{- if .Values.expose.config.tlsacme }} 4 | {{- if eq .Values.expose.config.tlsacme "true" }} 5 | apiVersion: v1 6 | data: 7 | tls.crt: Zm9vIC1uCg== 8 | tls.key: Zm9vIC1uCg== 9 | kind: Secret 10 | metadata: 11 | annotations: 12 | {{- if .Values.expose.production }} 13 | replicator.v1.mittwald.de/replicate-from: jx/tls-{{ .Values.expose.config.domain | replace "." "-" }}-p 14 | {{- else }} 15 | replicator.v1.mittwald.de/replicate-from: jx/tls-{{ .Values.expose.config.domain | replace "." "-" }}-s 16 | {{- end }} 17 | {{- if .Values.expose.production }} 18 | name: "tls-{{ .Values.expose.config.domain | replace "." "-" }}-p" 19 | {{- else }} 20 | name: "tls-{{ .Values.expose.config.domain | replace "." "-" }}-s" 21 | {{- end }} 22 | type: kubernetes.io/tls 23 | {{- end }} 24 | {{- end }} 25 | {{- end }} 26 | {{- end }} 27 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/env/values.yaml: -------------------------------------------------------------------------------- 1 | PipelineSecrets: {} 2 | cleanup: 3 | Annotations: 4 | helm.sh/hook: pre-delete 5 | helm.sh/hook-delete-policy: hook-succeeded 6 | Args: 7 | - --cleanup 8 | expose: 9 | Annotations: 10 | helm.sh/hook: post-install,post-upgrade 11 | helm.sh/hook-delete-policy: hook-succeeded 12 | Args: 13 | - --v 14 | - 4 15 | config: 16 | domain: 35.198.184.208.nip.io 17 | exposer: Ingress 18 | http: "true" 19 | jenkins: 20 | Servers: 21 | Global: 22 | EnvVars: 23 | TILLER_NAMESPACE: kube-system 24 | prow: {} 25 | -------------------------------------------------------------------------------- /chapter11/jx/watchlist-staging/jenkins-x.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - name: DEPLOY_NAMESPACE 3 | value: jx-staging 4 | pipelineConfig: 5 | env: 6 | - name: DEPLOY_NAMESPACE 7 | value: jx-staging 8 | pipelines: {} 9 | -------------------------------------------------------------------------------- /chapter11/k8s-dashboard/eks-admin.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: eks-admin 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1beta1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: eks-admin 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: eks-admin 18 | namespace: kube-system -------------------------------------------------------------------------------- /chapter11/microservices/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: watchlist 3 | description: Top 100 iMDB best movies in history 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 7 | maintainers: 8 | - name: Mohamed Labouardy 9 | email: mohamed@labouardy.com 10 | dependencies: 11 | - name: mongodb 12 | version: 7.8.10 13 | repository: https://charts.bitnami.com/bitnami 14 | alias: mongodb 15 | - name: movies-loader 16 | version: 1.0.0 17 | repository: https://mlabouardy.github.io/watchlist-charts 18 | - name: movies-parser 19 | version: 1.0.0 20 | repository: https://mlabouardy.github.io/watchlist-charts 21 | - name: movies-store 22 | version: 1.0.0 23 | repository: https://mlabouardy.github.io/watchlist-charts 24 | - name: movies-marketplace 25 | version: 1.0.0 26 | repository: https://mlabouardy.github.io/watchlist-charts -------------------------------------------------------------------------------- /chapter11/microservices/movies-loader/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ -------------------------------------------------------------------------------- /chapter11/microservices/movies-loader/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: movies-loader 3 | description: Loading movies.json to SQS 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 -------------------------------------------------------------------------------- /chapter11/microservices/movies-loader/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.namespace }}-movies-loader 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: {{ .Values.namespace }}-movies-loader 8 | data: 9 | AWS_REGION: {{ .Values.services.aws.region }} 10 | SQS_URL: https://sqs.{{ .Values.services.aws.region }}.amazonaws.com/{{ .Values.services.aws.account }}/movies_to_parse_{{ .Values.environment }} -------------------------------------------------------------------------------- /chapter11/microservices/movies-loader/values.yaml: -------------------------------------------------------------------------------- 1 | amespace: 'movies-loader' 2 | 3 | metadata: 4 | git: 5 | commitId: none 6 | jenkins: 7 | buildTag: none 8 | 9 | services: 10 | aws: 11 | account: '' 12 | region: '' 13 | registry: 14 | uri: 'ID.dkr.ecr.REGION.amazonaws.com' 15 | secret: '' 16 | 17 | deployment: 18 | tag: 'develop' 19 | -------------------------------------------------------------------------------- /chapter11/microservices/movies-marketplace/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ -------------------------------------------------------------------------------- /chapter11/microservices/movies-marketplace/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: movies-marketplace 3 | description: UI to browse top 100 iMDB movies 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 -------------------------------------------------------------------------------- /chapter11/microservices/movies-marketplace/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: movies-marketplace 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: movies-marketplace 8 | tier: frontend 9 | spec: 10 | selector: 11 | matchLabels: 12 | app: movies-marketplace 13 | template: 14 | metadata: 15 | name: movies-marketplace 16 | labels: 17 | app: movies-marketplace 18 | tier: frontend 19 | annotations: 20 | jenkins/build: {{ .Values.metadata.jenkins.buildTag | quote }} 21 | git/commitId: {{ .Values.metadata.git.commitId | quote }} 22 | spec: 23 | containers: 24 | - name: movies-marketplace 25 | image: "{{ .Values.services.registry.uri }}/mlabouardy/movies-marketplace:{{ .Values.deployment.tag }}" 26 | imagePullPolicy: Always 27 | {{- if .Values.services.registry.secret }} 28 | imagePullSecrets: 29 | - name: {{ .Values.services.registry.secret }} 30 | {{- end }} -------------------------------------------------------------------------------- /chapter11/microservices/movies-marketplace/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-marketplace 5 | namespace: {{ .Values.namespace }} 6 | spec: 7 | ports: 8 | - name: http 9 | port: 80 10 | targetPort: 80 11 | type: LoadBalancer 12 | selector: 13 | app: movies-marketplace -------------------------------------------------------------------------------- /chapter11/microservices/movies-marketplace/values.yaml: -------------------------------------------------------------------------------- 1 | namespace: 'movies-marketplace' 2 | 3 | metadata: 4 | git: 5 | commitId: none 6 | jenkins: 7 | buildTag: none 8 | 9 | services: 10 | registry: 11 | uri: 'ID.dkr.ecr.REGION.amazonaws.com' 12 | secret: '' 13 | 14 | deployment: 15 | tag: 'develop' -------------------------------------------------------------------------------- /chapter11/microservices/movies-parser/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ -------------------------------------------------------------------------------- /chapter11/microservices/movies-parser/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: movies-parser 3 | description: iMDB movies crawler & parser 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 -------------------------------------------------------------------------------- /chapter11/microservices/movies-parser/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ .Values.namespace }}-movies-parser 5 | namespace: {{ .Values.namespace }} 6 | labels: 7 | app: {{ .Values.namespace }}-movies-parser 8 | data: 9 | AWS_REGION: {{ .Values.services.aws.region }} 10 | SQS_URL: https://sqs.{{ .Values.services.aws.region }}.amazonaws.com/{{ .Values.services.aws.account }}/movies_to_parse_{{ .Values.environment }} -------------------------------------------------------------------------------- /chapter11/microservices/movies-parser/values.yaml: -------------------------------------------------------------------------------- 1 | namespace: 'movies-parser' 2 | 3 | metadata: 4 | git: 5 | commitId: none 6 | jenkins: 7 | buildTag: none 8 | 9 | services: 10 | aws: 11 | account: '' 12 | region: '' 13 | registry: 14 | uri: 'ID.dkr.ecr.REGION.amazonaws.com' 15 | secret: '' 16 | 17 | deployment: 18 | tag: 'develop' 19 | workers: 20 | replicas: 2 -------------------------------------------------------------------------------- /chapter11/microservices/movies-store/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /chapter11/microservices/movies-store/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: movies-store 3 | description: Movies store RESTful API 4 | type: application 5 | version: 1.0.0 6 | appVersion: 1.0.0 -------------------------------------------------------------------------------- /chapter11/microservices/movies-store/templates/secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: {{ .Values.namespace }}-secrets 5 | namespace: {{ .Values.namespace }} 6 | data: 7 | MONGO_URI: {{ .Values.services.mongodb.uri | b64enc }} 8 | MONGO_DATABASE : {{ .Values.mongodb.mongodbDatabase | b64enc }} 9 | MONGODB_USERNAME : {{ .Values.mongodb.mongodbUsername | b64enc }} 10 | MONGODB_PASSWORD : {{ .Values.mongodb.mongodbPassword | b64enc }} -------------------------------------------------------------------------------- /chapter11/microservices/movies-store/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: movies-store 5 | namespace: {{ .Values.namespace }} 6 | annotations: 7 | service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http 8 | {{- if .Values.deployment.https.ssl }} 9 | service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:{{ .Values.services.aws.region }}:{{ .Values.services.aws.account }}:certificate/{{ .Values.deployment.https.ssl }} 10 | service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" 11 | {{- end }} 12 | spec: 13 | ports: 14 | - name: http 15 | port: 80 16 | targetPort: 3000 17 | {{- if .Values.deployment.https.ssl }} 18 | - name: https 19 | port: 443 20 | targetPort: 3000 21 | {{- end }} 22 | type: LoadBalancer 23 | selector: 24 | app: movies-store -------------------------------------------------------------------------------- /chapter11/microservices/movies-store/values.yaml: -------------------------------------------------------------------------------- 1 | namespace: 'movies-store' 2 | 3 | metadata: 4 | git: 5 | commitId: none 6 | jenkins: 7 | buildTag: none 8 | 9 | mongodb: 10 | fullnameOverride: 'mongodb' 11 | mongodbRootPassword: '' 12 | mongodbUsername: '' 13 | mongodbDatabase: 'watchlist' 14 | 15 | services: 16 | aws: 17 | account: '' 18 | region: '' 19 | registry: 20 | uri: 'ID.dkr.ecr.REGION.amazonaws.com' 21 | secret: '' 22 | 23 | deployment: 24 | tag: 'develop' 25 | https: 26 | ssl: '' -------------------------------------------------------------------------------- /chapter11/values.overriden.yaml: -------------------------------------------------------------------------------- 1 | environment: 'sandbox' 2 | 3 | metadata: 4 | git: 5 | commitId: '1' 6 | jenkins: 7 | buildTag: '1' 8 | 9 | mongodb: 10 | mongodbUsername: 'watchlist' 11 | mongodbPassword: 'watchlist' 12 | 13 | services: 14 | aws: 15 | account: 'ID' 16 | region: 'REGION' 17 | mongodb: 18 | uri: 'mongodb://watchlist:watchlist@mongodb.default.svc.cluster.local/watchlist?retryWrites=true&w=majority&poolSize=1' 19 | registry: 20 | uri: 'ID.dkr.ecr.REGION.amazonaws.com' 21 | 22 | deployment: 23 | tag: 'develop' 24 | workers: 25 | replicas: 2 -------------------------------------------------------------------------------- /chapter12/functions/.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | main -------------------------------------------------------------------------------- /chapter12/functions/movies-loader/.gitignore: -------------------------------------------------------------------------------- 1 | deployment.zip -------------------------------------------------------------------------------- /chapter12/functions/movies-loader/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM python:3.7.3 2 | 3 | WORKDIR /app 4 | 5 | COPY requirements.txt . 6 | RUN pip install -r requirements.txt 7 | 8 | COPY test_index.py . 9 | COPY movies.json . -------------------------------------------------------------------------------- /chapter12/functions/movies-loader/index.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import os 4 | 5 | sqs = boto3.client('sqs', region_name=os.environ['AWS_REGION']) 6 | queue_url = os.environ['SQS_URL'] 7 | 8 | def sendMovie(movie): 9 | response = sqs.send_message( 10 | QueueUrl=queue_url, 11 | MessageBody=(json.dumps(movie))) 12 | return response['MessageId'] 13 | 14 | def handler(event, context): 15 | with open('movies.json') as json_file: 16 | data = json.load(json_file) 17 | for movie in data: 18 | print('Name: ' + movie['title']) 19 | message = sendMovie(movie) 20 | print('Message ID: ' + message) 21 | 22 | return { 23 | 'message' : 'done' 24 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-loader/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | unittest-xml-reporting -------------------------------------------------------------------------------- /chapter12/functions/movies-loader/test_index.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import json 3 | import xmlrunner 4 | 5 | class TestJSONLoaderMethods(unittest.TestCase): 6 | movies = [] 7 | 8 | @classmethod 9 | def setUpClass(cls): 10 | with open('movies.json') as json_file: 11 | cls.movies = json.load(json_file) 12 | 13 | def test_rank(self): 14 | self.assertEqual(self.movies[0]['rank'], '1') 15 | 16 | def test_title(self): 17 | self.assertEqual(self.movies[0]['title'], 'The Shawshank Redemption') 18 | 19 | def test_id(self): 20 | self.assertEqual(self.movies[0]['id'], 'tt0111161') 21 | 22 | if __name__ == '__main__': 23 | runner = xmlrunner.XMLTestRunner(output='reports') 24 | unittest.main(testRunner=runner) -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/.dockerignore: -------------------------------------------------------------------------------- 1 | nodes_modules 2 | coverage 3 | sonar-scanner-* 4 | .scannerwork 5 | # See http://help.github.com/ignore-files/ for more about ignoring files. 6 | 7 | # compiled output 8 | dist 9 | tmp 10 | out-tsc 11 | # Only exists if Bazel was run 12 | bazel-out 13 | 14 | # dependencies 15 | node_modules 16 | 17 | # profiling files 18 | chrome-profiler-events*.json 19 | speed-measure-plugin*.json 20 | 21 | # IDEs and editors 22 | .idea 23 | .project 24 | .classpath 25 | .c9/ 26 | *.launch 27 | .settings/ 28 | *.sublime-workspace 29 | 30 | # IDE - VSCode 31 | .vscode/* 32 | !.vscode/settings.json 33 | !.vscode/tasks.json 34 | !.vscode/launch.json 35 | !.vscode/extensions.json 36 | .history/* 37 | 38 | # misc 39 | .sass-cache 40 | connect.lock 41 | coverage 42 | libpeerconnection.log 43 | npm-debug.log 44 | yarn-error.log 45 | testem.log 46 | typings 47 | 48 | # System Files 49 | .DS_Store 50 | Thumbs.db 51 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | dist 5 | tmp 6 | .scannerwork 7 | out-tsc 8 | *.tgz 9 | sonar-scanner-* 10 | # Only exists if Bazel was run 11 | bazel-out 12 | 13 | # dependencies 14 | node_modules 15 | 16 | # profiling files 17 | chrome-profiler-events*.json 18 | speed-measure-plugin*.json 19 | 20 | # IDEs and editors 21 | .idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | .history/* 36 | 37 | # misc 38 | .sass-cache 39 | connect.lock 40 | coverage 41 | libpeerconnection.log 42 | npm-debug.log 43 | yarn-error.log 44 | testem.log 45 | typings 46 | 47 | # System Files 48 | .DS_Store 49 | Thumbs.db 50 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.0.0 as builder 2 | ARG ENVIRONMENT 3 | ENV CHROME_BIN=chromium 4 | WORKDIR /app 5 | RUN apt-get update && apt-get install -y chromium 6 | COPY package-lock.json . 7 | COPY package.json . 8 | RUN npm i && npm i -g @angular/cli 9 | COPY . . 10 | RUN ng build -c $ENVIRONMENT -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM node:14.0.0 2 | 3 | ENV CHROME_BIN=chromium 4 | 5 | WORKDIR /app 6 | 7 | RUN apt-get update && apt-get install -y chromium 8 | 9 | COPY package-lock.json . 10 | COPY package.json . 11 | 12 | RUN npm i && npm i -g @angular/cli 13 | 14 | COPY . . -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/README.md: -------------------------------------------------------------------------------- 1 | # Movies WatchList 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.3.19. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | # Author 10 | 11 | Mohamed Labouardy -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('marketplace app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=angular:movies-marketplace 2 | sonar.projectName=movies-marketplace 3 | sonar.projectVersion=1.0.0 4 | sonar.sourceEncoding=UTF-8 5 | sonar.sources=src 6 | sonar.exclusions=**/node_modules/**,**/*.spec.ts 7 | sonar.tests=src/app 8 | sonar.test.inclusions=**/*.spec.ts 9 | sonar.ts.tslint.configPath=tslint.json 10 | sonar.javascript.lcov.reportPaths=coverage/lcov.info -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { HttpModule } from '@angular/http'; 3 | import { ApiService } from './api.service'; 4 | 5 | describe('ApiService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({ 7 | imports: [HttpModule] 8 | })); 9 | 10 | it('should be created', () => { 11 | const service: ApiService = TestBed.get(ApiService); 12 | expect(service).toBeTruthy(); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { DashboardComponent } from './dashboard/dashboard.component'; 4 | import { FavoritesComponent } from './favorites/favorites.component'; 5 | import { MovieComponent } from './movie/movie.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: 'dashboard', 10 | component: DashboardComponent, 11 | data: { title: 'Sign In - Komiser' } 12 | }, 13 | { 14 | path: 'favorites', 15 | component: FavoritesComponent, 16 | data: { title: 'Join - Komiser' } 17 | }, 18 | { 19 | path: 'movies/:id', 20 | component: MovieComponent, 21 | data: { title: 'Expired token - Komiser' } 22 | }, 23 | { path: '**', 24 | redirectTo: 'dashboard' 25 | } 26 | ]; 27 | 28 | @NgModule({ 29 | imports: [ 30 | RouterModule.forRoot(routes) 31 | ], 32 | exports: [RouterModule] 33 | }) 34 | export class AppRoutingModule { } 35 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | it('should render title', () => { 23 | const fixture = TestBed.createComponent(AppComponent); 24 | fixture.detectChanges(); 25 | const compiled = fixture.debugElement.nativeElement; 26 | expect(compiled.querySelector('.toolbar span').textContent).toContain('Watchlist'); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'marketplace'; 10 | } 11 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpModule } from '@angular/http'; 4 | import { FormsModule } from '@angular/forms'; 5 | 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { AppComponent } from './app.component'; 8 | import { DashboardComponent } from './dashboard/dashboard.component'; 9 | import { FavoritesComponent } from './favorites/favorites.component'; 10 | import { MovieComponent } from './movie/movie.component'; 11 | 12 | import { ApiService } from './api.service'; 13 | 14 | @NgModule({ 15 | declarations: [ 16 | AppComponent, 17 | DashboardComponent, 18 | FavoritesComponent, 19 | MovieComponent 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | AppRoutingModule, 24 | HttpModule, 25 | FormsModule 26 | ], 27 | providers: [ 28 | ApiService 29 | ], 30 | bootstrap: [AppComponent] 31 | }) 32 | export class AppModule { } 33 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | .card-text{ 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | height: 100px;; 5 | } 6 | 7 | .card-title{ 8 | height: 50px; 9 | font-size: 1.1rem; 10 | } 11 | 12 | .card{ 13 | margin-top: 5px; 14 | margin-bottom: 5px; 15 | } 16 | 17 | .search-btn{ 18 | margin-left: 5px; 19 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { FormsModule } from '@angular/forms'; 3 | import { DashboardComponent } from './dashboard.component'; 4 | import { RouterModule } from '@angular/router'; 5 | import { HttpModule } from '@angular/http'; 6 | 7 | describe('DashboardComponent', () => { 8 | let component: DashboardComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [FormsModule, RouterModule, HttpModule], 14 | declarations: [ DashboardComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(DashboardComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/favorites/favorites.component.css: -------------------------------------------------------------------------------- 1 | .card-text{ 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | height: 100px;; 5 | } 6 | 7 | .card-title{ 8 | height: 50px; 9 | font-size: 1.1rem; 10 | } 11 | 12 | .card{ 13 | margin-top: 5px; 14 | margin-bottom: 5px; 15 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/favorites/favorites.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { RouterModule } from '@angular/router'; 3 | import { HttpModule } from '@angular/http'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | import { FavoritesComponent } from './favorites.component'; 6 | 7 | describe('FavoritesComponent', () => { 8 | let component: FavoritesComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [RouterModule, HttpModule, RouterTestingModule], 14 | declarations: [ FavoritesComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(FavoritesComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/favorites/favorites.component.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import { Component, OnInit } from '@angular/core'; 3 | import { ApiService } from '../api.service'; 4 | 5 | @Component({ 6 | selector: 'app-favorites', 7 | templateUrl: './favorites.component.html', 8 | styleUrls: ['./favorites.component.css'] 9 | }) 10 | export class FavoritesComponent implements OnInit { 11 | public movies: Array = new Array(); 12 | 13 | constructor(private apiService: ApiService) { 14 | this.getMovies(); 15 | } 16 | 17 | private getMovies(){ 18 | this.apiService.getFavorites().subscribe(data => { 19 | this.movies = data; 20 | }, err => { 21 | this.movies = []; 22 | }); 23 | } 24 | 25 | ngOnInit() { 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/movie/movie.component.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | margin-right: 3px; 3 | } 4 | 5 | .title{ 6 | font-weight: 900; 7 | margin-bottom: 10px; 8 | margin-top:10px; 9 | } 10 | 11 | .video{ 12 | margin-right: 10px; 13 | } 14 | 15 | .actions{ 16 | margin-bottom: 20px; 17 | } 18 | 19 | .details{ 20 | margin-bottom: 20px; 21 | } 22 | 23 | .similar{ 24 | margin-right: 5px; 25 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/app/movie/movie.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | import { HttpModule } from '@angular/http'; 3 | import { RouterModule } from '@angular/router'; 4 | import { RouterTestingModule } from '@angular/router/testing'; 5 | import { MovieComponent } from './movie.component'; 6 | 7 | describe('MovieComponent', () => { 8 | let component: MovieComponent; 9 | let fixture: ComponentFixture; 10 | 11 | beforeEach(async(() => { 12 | TestBed.configureTestingModule({ 13 | imports: [HttpModule, RouterModule, RouterTestingModule], 14 | declarations: [ MovieComponent ] 15 | }) 16 | .compileComponents(); 17 | })); 18 | 19 | beforeEach(() => { 20 | fixture = TestBed.createComponent(MovieComponent); 21 | component = fixture.componentInstance; 22 | fixture.detectChanges(); 23 | }); 24 | 25 | it('should create', () => { 26 | expect(component).toBeTruthy(); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter12/functions/movies-marketplace/src/assets/.gitkeep -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter12/functions/movies-marketplace/src/assets/images/logo.png -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/environments/environment.production.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | name: 'production', 4 | apiURL: 'https://rth65vizrb.execute-api.eu-west-3.amazonaws.com/production', 5 | }; 6 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/environments/environment.sandbox.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | name: 'sandbox', 4 | apiURL: 'https://rth65vizrb.execute-api.eu-west-3.amazonaws.com/sandbox', 5 | }; 6 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/environments/environment.staging.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | name: 'staging', 4 | apiURL: 'https://rth65vizrb.execute-api.eu-west-3.amazonaws.com/staging', 5 | }; 6 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | name: 'sandbox', 8 | apiURL: '', 9 | }; 10 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter12/functions/movies-marketplace/src/favicon.ico -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter12/functions/movies-marketplace/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /chapter12/functions/movies-parser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.4 2 | WORKDIR /go/src/github.com/mlabouardy/movies-parser 3 | COPY main.go . 4 | RUN go get -v 5 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main main.go -------------------------------------------------------------------------------- /chapter12/functions/movies-parser/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.4 2 | 3 | ENV VERSION v0.2.0 4 | ENV GOCACHE /tmp 5 | WORKDIR /go/src/github/mlabouardy/movies-parser 6 | 7 | RUN wget https://github.com/sonatype-nexus-community/nancy/releases/download/$VERSION/nancy-linux.amd64-$VERSION -O nancy && \ 8 | chmod +x nancy && mv nancy /usr/local/bin/nancy 9 | RUN go get -u golang.org/x/lint/golint 10 | 11 | COPY . . 12 | 13 | RUN go get -v -------------------------------------------------------------------------------- /chapter12/functions/movies-parser/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/aws/aws-lambda-go" 30 | version = "1.17.0" 31 | 32 | [[constraint]] 33 | name = "github.com/aws/aws-sdk-go-v2" 34 | version = "0.22.0" 35 | 36 | [prune] 37 | go-tests = true 38 | unused-packages = true -------------------------------------------------------------------------------- /chapter12/functions/movies-store/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output -------------------------------------------------------------------------------- /chapter12/functions/movies-store/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "mocha": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2018 15 | }, 16 | "rules": { 17 | "no-unused-vars": "off" 18 | } 19 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-store/.gitignore: -------------------------------------------------------------------------------- 1 | deployment.zip -------------------------------------------------------------------------------- /chapter12/functions/movies-store/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.0.0 2 | 3 | WORKDIR /app 4 | 5 | COPY package-lock.json . 6 | COPY package.json . 7 | RUN npm i --only=prod -------------------------------------------------------------------------------- /chapter12/functions/movies-store/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM node:14.0.0 2 | 3 | WORKDIR /app 4 | 5 | COPY package-lock.json . 6 | COPY package.json . 7 | RUN npm i 8 | 9 | COPY . . -------------------------------------------------------------------------------- /chapter12/functions/movies-store/Jenkinsfile.declarative: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent{ 3 | label 'workers' 4 | } 5 | 6 | stages { 7 | stage('Checkout'){ 8 | steps{ 9 | checkout scm 10 | } 11 | } 12 | stage('Unit Tests'){} 13 | stage('Build'){} 14 | stage('Push'){} 15 | } 16 | post { 17 | always { 18 | if (env.BRANCH_NAME == 'master'){ 19 | sendEmail(currentBuild.currentResult) 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /chapter12/functions/movies-store/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movies-store", 3 | "version": "1.0.0", 4 | "description": "Movies store API", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha ./test/*.spec.js", 8 | "lint": "eslint .", 9 | "coverage": "nyc --reporter=html mocha" 10 | }, 11 | "author": "Mohamed Labouardy", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "aws-sdk-mock": "^5.1.0", 15 | "chai": "^4.2.0", 16 | "eslint": "^6.8.0", 17 | "fs": "0.0.1-security", 18 | "mocha": "^7.2.0", 19 | "nyc": "^15.0.1" 20 | }, 21 | "dependencies": { 22 | "aws-sdk": "^2.814.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter12/functions/movies-store/test/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id" : { 4 | "S" : "1" 5 | }, 6 | "title" : { 7 | "S" : "John Wick" 8 | }, 9 | "rank" : { 10 | "S" : "9" 11 | }, 12 | "rating" : { 13 | "S" : "7.4/10" 14 | }, 15 | "releaseDate" : { 16 | "S" : "24 October 2014 (USA)" 17 | }, 18 | "description" : { 19 | "S" : "An ex-hit-man comes out of retirement to track down the gangsters that killed his dog and took everything from him." 20 | }, 21 | "poster" : { 22 | "S" : "https://m.media-amazon.com/images/M/MV5BMTU2NjA1ODgzMF5BMl5BanBnXkFtZTgwMTM2MTI4MjE@._V1_UX182_CR0,0,182,268_AL_.jpg" 23 | }, 24 | "actors" : { 25 | "S" : "[]" 26 | }, 27 | "videos" : { 28 | "S" : "[]" 29 | }, 30 | "similar" : { 31 | "S" : "[]" 32 | } 33 | } 34 | ] -------------------------------------------------------------------------------- /chapter12/functions/movies-store/test/movies.spec.js: -------------------------------------------------------------------------------- 1 | const Expect = require('chai').expect; 2 | const AWSMock = require('aws-sdk-mock'); 3 | const AWS = require('aws-sdk'); 4 | const FS = require('fs'); 5 | 6 | let movies = JSON.parse(FS.readFileSync('test/data.json', 'utf8')); 7 | 8 | AWSMock.setSDKInstance(AWS); 9 | 10 | AWSMock.mock('DynamoDB', 'scan', function (params, callback){ 11 | callback(null, movies); 12 | }); 13 | 14 | const DynamoDB = new AWS.DynamoDB({apiVersion: '2012-08-10'}); 15 | 16 | describe('Movies', () => { 17 | before(() => { 18 | process.env.TABLE_NAME = 'Movies' 19 | }) 20 | 21 | it('should get movies', () => { 22 | let params = { 23 | TableName: process.env.TABLE_NAME, 24 | } 25 | 26 | DynamoDB.scan(params, (err, data) => { 27 | Expect(data.length).to.equal(1) 28 | Expect(err).to.equal(null) 29 | }); 30 | }) 31 | }) -------------------------------------------------------------------------------- /chapter12/terraform/deployment.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter12/terraform/deployment.zip -------------------------------------------------------------------------------- /chapter12/terraform/events.tf: -------------------------------------------------------------------------------- 1 | resource "aws_lambda_event_source_mapping" "sqs_trigger" { 2 | event_source_arn = aws_sqs_queue.queue.arn 3 | function_name = module.MoviesParser.arn 4 | batch_size = 5 5 | } -------------------------------------------------------------------------------- /chapter12/terraform/modules/function/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "role" { 2 | name = "${var.name}Role" 3 | 4 | assume_role_policy = < 5000 4 | type => jenkins 5 | } 6 | } 7 | 8 | filter { 9 | if [type] == "jenkins" { 10 | grok { 11 | patterns_dir => ["/etc/logstash/patterns"] 12 | match => { 13 | "message" => "%{TIMESTAMP_ISO8601:createdAt}%{SPACE}\[id=%{INT:buildId}\]%{SPACE}%{LOGLEVEL:level}%{SPACE}%{JAVACLASS:class}%{DATA:state}:%{SPACE}%{JOBNAME:project} #%{NUMBER:buildNumber} %{DATA:execution}: %{WORD:status}" 14 | } 15 | } 16 | } 17 | } 18 | 19 | output { 20 | if [type] == "jenkins" { 21 | elasticsearch { 22 | hosts => 'https://elasticsearch.domain.com:443' 23 | index => 'jenkins-%{+YYYY.MM.dd}' 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /chapter13/logstash/packer/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java & unzip" 4 | add-apt-repository ppa:openjdk-r/ppa 5 | apt update 6 | apt install -y openjdk-11-jdk unzip 7 | 8 | echo "Install Logstash" 9 | wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add - 10 | apt-get install -y apt-transport-https 11 | echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | tee -a /etc/apt/sources.list.d/elastic-7.x.list 12 | apt update 13 | apt install -y logstash 14 | mv /tmp/jenkins.conf /etc/logstash/conf.d/jenkins.conf 15 | 16 | echo "Start ElasticSearch" 17 | systemctl daemon-reload 18 | systemctl enable logstash.service 19 | systemctl start logstash.service -------------------------------------------------------------------------------- /chapter13/logstash/packer/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "AWS REGION", 4 | "source_ami": "UBUNTU SERVER 18.04 LTS ID", 5 | "profile": "AWS PROFILE", 6 | "instance_type": "AWS INSTANCE TYPE" 7 | }, 8 | "builders" : [ 9 | { 10 | "type" : "amazon-ebs", 11 | "profile" : "{{user `profile`}}", 12 | "region" : "{{user `region`}}", 13 | "instance_type" : "{{user `instance_type`}}", 14 | "source_ami" : "{{user `source_ami`}}", 15 | "ssh_username" : "ubuntu", 16 | "ami_name" : "logstash-7.0.0", 17 | "ami_description" : "Amazon Linux Image with Logstash" 18 | } 19 | ], 20 | "provisioners" : [ 21 | { 22 | "type" : "file", 23 | "source" : "./jenkins.conf", 24 | "destination" : "/tmp/jenkins.conf" 25 | }, 26 | { 27 | "type" : "shell", 28 | "script" : "./setup.sh", 29 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /chapter13/logstash/terraform/dns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_record" "logstash" { 2 | zone_id = var.hosted_zone_id 3 | name = "logstash.${var.domain_name}" 4 | type = "A" 5 | 6 | alias { 7 | name = aws_elb.logstash_elb.dns_name 8 | zone_id = aws_elb.logstash_elb.zone_id 9 | evaluate_target_health = true 10 | } 11 | } -------------------------------------------------------------------------------- /chapter13/logstash/terraform/loadbalancers.tf: -------------------------------------------------------------------------------- 1 | // logstash ELB 2 | resource "aws_elb" "logstash_elb" { 3 | subnets = var.public_subnets 4 | cross_zone_load_balancing = true 5 | security_groups = [aws_security_group.elb_logstash_sg.id] 6 | instances = [aws_instance.logstash.id] 7 | 8 | listener { 9 | instance_port = 5000 10 | instance_protocol = "http" 11 | lb_port = 443 12 | lb_protocol = "https" 13 | ssl_certificate_id = var.ssl_arn 14 | } 15 | 16 | health_check { 17 | healthy_threshold = 2 18 | unhealthy_threshold = 2 19 | timeout = 3 20 | target = "TCP:5000" 21 | interval = 5 22 | } 23 | 24 | tags = { 25 | Name = "logstash_elb" 26 | Author = var.author 27 | } 28 | } -------------------------------------------------------------------------------- /chapter13/logstash/terraform/logstash.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "logstash" { 2 | most_recent = true 3 | owners = ["self"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["logstash-*"] 8 | } 9 | } 10 | 11 | resource "aws_instance" "logstash" { 12 | ami = data.aws_ami.logstash.id 13 | instance_type = var.logstash_instance_type 14 | key_name = var.key_name 15 | vpc_security_group_ids = [aws_security_group.logstash_sg.id] 16 | subnet_id = element(var.private_subnets, 0) 17 | 18 | root_block_device { 19 | volume_type = "gp2" 20 | volume_size = 20 21 | delete_on_termination = false 22 | } 23 | 24 | tags = { 25 | Author = var.author 26 | Name = "logstash" 27 | Stack = "Logging" 28 | } 29 | } -------------------------------------------------------------------------------- /chapter13/logstash/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "logstash" { 2 | value = "https://${aws_route53_record.logstash.name}" 3 | } -------------------------------------------------------------------------------- /chapter13/logstash/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter13/prometheus/packer/prometheus.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Prometheus 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | User=prometheus 8 | Group=prometheus 9 | Type=simple 10 | ExecStart=/usr/local/bin/prometheus \ 11 | --config.file /etc/prometheus/prometheus.yml \ 12 | --storage.tsdb.path /var/lib/prometheus/ \ 13 | --web.console.templates=/etc/prometheus/consoles \ 14 | --web.console.libraries=/etc/prometheus/console_libraries 15 | 16 | [Install] 17 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /chapter13/prometheus/packer/prometheus.yml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 10s 3 | 4 | scrape_configs: 5 | - job_name: 'prometheus_master' 6 | scrape_interval: 5s 7 | static_configs: 8 | - targets: ['localhost:9090'] 9 | - job_name: 'jenkins' 10 | metrics_path: '/prometheus/' 11 | scheme: https 12 | static_configs: 13 | - targets: ['jenkins.domain.com'] -------------------------------------------------------------------------------- /chapter13/prometheus/terraform/dns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_record" "prometheus" { 2 | zone_id = var.hosted_zone_id 3 | name = "prometheus.${var.domain_name}" 4 | type = "A" 5 | 6 | alias { 7 | name = aws_elb.prometheus_elb.dns_name 8 | zone_id = aws_elb.prometheus_elb.zone_id 9 | evaluate_target_health = true 10 | } 11 | } -------------------------------------------------------------------------------- /chapter13/prometheus/terraform/loadbalancers.tf: -------------------------------------------------------------------------------- 1 | // prometheus ELB 2 | resource "aws_elb" "prometheus_elb" { 3 | subnets = [for subnet in aws_subnet.public_subnets : subnet.id] 4 | cross_zone_load_balancing = true 5 | security_groups = [aws_security_group.elb_prometheus_sg.id] 6 | instances = [aws_instance.prometheus.id] 7 | 8 | listener { 9 | instance_port = 9090 10 | instance_protocol = "http" 11 | lb_port = 443 12 | lb_protocol = "https" 13 | ssl_certificate_id = var.ssl_arn 14 | } 15 | 16 | health_check { 17 | healthy_threshold = 2 18 | unhealthy_threshold = 2 19 | timeout = 3 20 | target = "TCP:9090" 21 | interval = 5 22 | } 23 | 24 | tags = { 25 | Name = "prometheus_elb" 26 | Author = var.author 27 | } 28 | } -------------------------------------------------------------------------------- /chapter13/prometheus/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "prometheus" { 2 | value = "https://${aws_route53_record.prometheus.name}" 3 | } -------------------------------------------------------------------------------- /chapter13/prometheus/terraform/prometheus.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "prometheus" { 2 | most_recent = true 3 | owners = ["self"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["prometheus-*"] 8 | } 9 | } 10 | 11 | resource "aws_instance" "prometheus" { 12 | ami = data.aws_ami.prometheus.id 13 | instance_type = var.prometheus_instance_type 14 | key_name = aws_key_pair.management.id 15 | vpc_security_group_ids = [aws_security_group.prometheus_sg.id] 16 | subnet_id = element(aws_subnet.private_subnets, 0).id 17 | 18 | root_block_device { 19 | volume_type = "gp2" 20 | volume_size = 20 21 | delete_on_termination = false 22 | } 23 | 24 | tags = { 25 | Author = var.author 26 | Name = "prometheus" 27 | Stack = "Monitoring" 28 | } 29 | } -------------------------------------------------------------------------------- /chapter13/prometheus/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter13/telegraf/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | wget https://dl.influxdata.com/telegraf/releases/telegraf-1.19.0-1.x86_64.rpm 3 | yum localinstall -y telegraf-1.19.0-1.x86_64.rpm 4 | systemctl enable telegraf 5 | systemctl restart telegraf -------------------------------------------------------------------------------- /chapter14/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jenkins/jenkins:lts 2 | MAINTAINER mlabouardy 3 | 4 | USER root 5 | RUN apt-get update && apt-get install -y apt-transport-https \ 6 | ca-certificates curl gnupg2 \ 7 | software-properties-common 8 | RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - 9 | RUN apt-key fingerprint 0EBFCD88 10 | RUN add-apt-repository \ 11 | "deb [arch=amd64] https://download.docker.com/linux/debian \ 12 | $(lsb_release -cs) stable" 13 | RUN apt-get update && apt-get install -y docker-ce-cli 14 | USER jenkins 15 | RUN jenkins-plugin-cli --plugins blueocean:1.24.3 workflow-aggregator:2.6 github:1.33.1 docker-plugin:1.2.1 16 | -------------------------------------------------------------------------------- /chapter14/backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd $JENKINS_HOME 4 | BACKUP_TIME=$(date +'%m.%d.%Y') 5 | zip -r backup-${BACKUP_TIME} . 6 | aws s3 cp backup-${BACKUP_TIME} s3://BUCKET/ -------------------------------------------------------------------------------- /chapter14/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | docker: 5 | image: docker:dind 6 | ports: 7 | - "2376:2376" 8 | networks: 9 | jenkins: 10 | aliases: 11 | - docker 12 | environment: 13 | - DOCKER_TLS_CERTDIR=/certs 14 | volumes: 15 | - jenkins-docker-certs:/certs/client 16 | - jenkins-data:/var/jenkins_home 17 | privileged: true 18 | 19 | jenkins: 20 | build: . 21 | ports: 22 | - "8080:8080" 23 | - "50000:50000" 24 | networks: 25 | - jenkins 26 | environment: 27 | - DOCKER_HOST=tcp://docker:2376 28 | - DOCKER_CERT_PATH=/certs/client 29 | - DOCKER_TLS_VERIFY=1 30 | volumes: 31 | - jenkins-data:/var/jenkins_home 32 | - jenkins-docker-certs:/certs/client:ro 33 | 34 | volumes: 35 | jenkins-docker-certs: {} 36 | jenkins-data: {} 37 | 38 | networks: 39 | jenkins: -------------------------------------------------------------------------------- /chapter14/library/vars/commitAuthor.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | def call() { 4 | sh 'git show -s --pretty=%an > .git/commitAuthor' 5 | def commitAuthor = readFile('.git/commitAuthor').trim() 6 | sh 'rm .git/commitAuthor' 7 | commitAuthor 8 | } -------------------------------------------------------------------------------- /chapter14/library/vars/commitID.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | def call() { 4 | sh 'git rev-parse HEAD > .git/commitID' 5 | def commitID = readFile('.git/commitID').trim() 6 | sh 'rm .git/commitID' 7 | commitID 8 | } -------------------------------------------------------------------------------- /chapter14/library/vars/commitMessage.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | def call() { 4 | sh 'git log --format=%B -n 1 HEAD > .git/commitMessage' 5 | def commitMessage = readFile('.git/commitMessage').trim() 6 | sh 'rm .git/commitMessage' 7 | commitMessage 8 | } -------------------------------------------------------------------------------- /chapter14/library/vars/notifySlack.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | 3 | def call(String buildStatus) { 4 | buildStatus = buildStatus ?: 'SUCCESSFUL' 5 | def colorCode = '#FF0000' 6 | def subject = "Name: '${env.JOB_NAME}'\nStatus: ${buildStatus}\nBuild ID: ${env.BUILD_NUMBER}" 7 | def summary = "${subject}\nMessage: ${commitMessage()}\nAuthor: ${commitAuthor()}\nURL: ${env.BUILD_URL}" 8 | 9 | if (buildStatus == 'STARTED') { 10 | colorCode = '#546e7a' 11 | } else if (buildStatus == 'SUCCESSFUL') { 12 | colorCode = '#2e7d32' 13 | } else { 14 | colorCode = '#c62828c' 15 | } 16 | 17 | slackSend (color: colorCode, message: summary) 18 | } -------------------------------------------------------------------------------- /chapter14/src/com/labouardy/utils/Git.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | package com.labouardy.utils 3 | 4 | class Git { 5 | 6 | Git(){} 7 | 8 | def commitAuthor() { 9 | sh 'git show -s --pretty=%an > .git/commitAuthor' 10 | def commitAuthor = readFile('.git/commitAuthor').trim() 11 | sh 'rm .git/commitAuthor' 12 | commitAuthor 13 | } 14 | 15 | def commitID() { 16 | sh 'git rev-parse HEAD > .git/commitID' 17 | def commitID = readFile('.git/commitID').trim() 18 | sh 'rm .git/commitID' 19 | commitID 20 | } 21 | 22 | def commitMessage() { 23 | sh 'git log --format=%B -n 1 HEAD > .git/commitMessage' 24 | def commitMessage = readFile('.git/commitMessage').trim() 25 | sh 'rm .git/commitMessage' 26 | commitMessage 27 | } 28 | } -------------------------------------------------------------------------------- /chapter2/Jenkinsfile.scripted: -------------------------------------------------------------------------------- 1 | def imageName = 'example' 2 | 3 | pipeline{ 4 | agent{ 5 | label 'workers' 6 | } 7 | 8 | stages{ 9 | stage('Checkout'){ 10 | steps{ 11 | checkout scm 12 | } 13 | } 14 | 15 | stage('Build'){ 16 | docker.build(imageName) 17 | } 18 | 19 | stage('Deploy'){ 20 | echo "Deploying ..." 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /chapter3/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline{ 2 | agent{ 3 | label 'workers' 4 | } 5 | stages{ 6 | stage('Checkout'){} 7 | } 8 | } 9 | 10 | node('workers'){ 11 | stage('Checkout'){} 12 | } -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/basic-security.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.security.* 5 | 6 | def instance = Jenkins.getInstance() 7 | 8 | println "--> creating local user 'USERNAME'" 9 | 10 | def hudsonRealm = new HudsonPrivateSecurityRealm(false) 11 | hudsonRealm.createAccount('USERNAME','PASSWORD') 12 | instance.setSecurityRealm(hudsonRealm) 13 | 14 | def strategy = new FullControlOnceLoggedInAuthorizationStrategy() 15 | instance.setAuthorizationStrategy(strategy) 16 | instance.save() -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/csrf-protection.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import hudson.security.csrf.DefaultCrumbIssuer 4 | import jenkins.model.Jenkins 5 | 6 | println "--> enabling CSRF protection" 7 | 8 | def instance = Jenkins.instance 9 | instance.setCrumbIssuer(new DefaultCrumbIssuer(true)) 10 | instance.save() -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/disable-cli.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | 5 | Jenkins jenkins = Jenkins.getInstance() 6 | 7 | println "--> disabling Jenkins remote CLI" 8 | jenkins.CLI.get().setEnabled(false) 9 | jenkins.save() -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/disable-jnlp.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | import jenkins.security.s2m.* 5 | 6 | Jenkins jenkins = Jenkins.getInstance() 7 | 8 | println "--> disabling JNLP" 9 | jenkins.setSlaveAgentPort(-1) 10 | 11 | println "--> disabling non-encrypted protocols" 12 | HashSet newProtocols = new HashSet<>(jenkins.getAgentProtocols()); 13 | newProtocols.removeAll(Arrays.asList( 14 | "JNLP3-connect", "JNLP2-connect", "JNLP-connect", "CLI-connect" 15 | )); 16 | jenkins.setAgentProtocols(newProtocols); 17 | jenkins.save() -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/node-agent.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.model.* 2 | import com.cloudbees.plugins.credentials.* 3 | import com.cloudbees.plugins.credentials.common.* 4 | import com.cloudbees.plugins.credentials.domains.* 5 | import com.cloudbees.plugins.credentials.impl.* 6 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.* 7 | import hudson.plugins.sshslaves.*; 8 | 9 | println "--> creating SSH credentials" 10 | 11 | domain = Domain.global() 12 | store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 13 | 14 | slavesPrivateKey = new BasicSSHUserPrivateKey( 15 | CredentialsScope.GLOBAL, 16 | "jenkins-slaves", 17 | "ec2-user", 18 | new BasicSSHUserPrivateKey.UsersPrivateKeySource(), 19 | "", 20 | "" 21 | ) 22 | 23 | store.addCredentials(domain, slavesPrivateKey) -------------------------------------------------------------------------------- /chapter4/distributed/master/scripts/skip-jenkins-setup.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.util.*; 5 | import jenkins.install.*; 6 | 7 | def instance = Jenkins.getInstance() 8 | 9 | instance.setInstallState(InstallState.INITIAL_SETUP_COMPLETED) -------------------------------------------------------------------------------- /chapter4/distributed/worker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java JDK 8" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | 7 | echo "Install Docker engine" 8 | yum update -y 9 | yum install docker -y 10 | usermod -aG docker ec2-user 11 | systemctl enable docker 12 | 13 | echo "Install git" 14 | yum install -y git -------------------------------------------------------------------------------- /chapter4/distributed/worker/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "AWS REGION", 4 | "aws_profile": "AWS PROFILE", 5 | "source_ami" : "AMAZON LINUX AMI 2 ID", 6 | "instance_type": "INSTANCE TYPE" 7 | }, 8 | "builders" : [ 9 | { 10 | "type" : "amazon-ebs", 11 | "profile" : "{{user `aws_profile`}}", 12 | "region" : "{{user `region`}}", 13 | "instance_type" : "{{user `instance_type`}}", 14 | "source_ami" : "{{user `source_ami`}}", 15 | "ssh_username" : "ec2-user", 16 | "ami_name" : "jenkins-worker1", 17 | "ami_description" : "Jenkins worker's AMI", 18 | "run_tags" : { 19 | "Name" : "packer-builder" 20 | } 21 | } 22 | ], 23 | "provisioners" : [ 24 | { 25 | "type" : "shell", 26 | "script" : "./setup.sh", 27 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /chapter4/standalone/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Jenkins stable release" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo 7 | rpm --import http://pkg.jenkins.io/redhat-stable/jenkins.io.key 8 | yum install -y jenkins 9 | chkconfig jenkins on 10 | service jenkins start -------------------------------------------------------------------------------- /chapter4/standalone/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "AWS REGION", 4 | "aws_profile": "AWS PROFILE", 5 | "source_ami" : "AMAZON LINUX AMI 2 ID", 6 | "instance_type": "INSTANCE TYPE" 7 | }, 8 | "builders" : [ 9 | { 10 | "type" : "amazon-ebs", 11 | "profile" : "{{user `aws_profile`}}", 12 | "region" : "{{user `region`}}", 13 | "instance_type" : "{{user `instance_type`}}", 14 | "source_ami" : "{{user `source_ami`}}", 15 | "ssh_username" : "ec2-user", 16 | "ami_name" : "jenkins-master-2.204.1", 17 | "ami_description" : "Amazon Linux Image with Jenkins Server", 18 | "run_tags" : { 19 | "Name" : "packer-builder" 20 | } 21 | } 22 | ], 23 | "provisioners" : [ 24 | { 25 | "type" : "shell", 26 | "script" : "./setup.sh", 27 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /chapter5/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bastion" { 2 | value = aws_instance.bastion.public_ip 3 | } 4 | 5 | output "jenkins-master-elb" { 6 | value = aws_elb.jenkins_elb.dns_name 7 | } 8 | 9 | output "jenkins-dns" { 10 | value = "https://${aws_route53_record.jenkins_master.name}" 11 | } -------------------------------------------------------------------------------- /chapter5/private_rt.tf: -------------------------------------------------------------------------------- 1 | // Static IP for Nat Gateway 2 | resource "aws_eip" "nat" { 3 | vpc = true 4 | 5 | tags = { 6 | Name = "eip-nat_${var.vpc_name}" 7 | Author = var.author 8 | } 9 | } 10 | 11 | // Nat Gateway 12 | resource "aws_nat_gateway" "nat" { 13 | allocation_id = aws_eip.nat.id 14 | subnet_id = element(aws_subnet.public_subnets.*.id, 0) 15 | 16 | tags = { 17 | Name = "nat_${var.vpc_name}" 18 | Author = var.author 19 | } 20 | } 21 | 22 | // Private Route Table 23 | resource "aws_route_table" "private_rt" { 24 | vpc_id = aws_vpc.management.id 25 | 26 | route { 27 | cidr_block = "0.0.0.0/0" 28 | nat_gateway_id = aws_nat_gateway.nat.id 29 | } 30 | 31 | tags = { 32 | Name = "private_rt_${var.vpc_name}" 33 | Author = var.author 34 | } 35 | } 36 | 37 | // Associate private subnets to private route table 38 | resource "aws_route_table_association" "private" { 39 | count = var.private_subnets_count 40 | subnet_id = element(aws_subnet.private_subnets.*.id, count.index) 41 | route_table_id = aws_route_table.private_rt.id 42 | } -------------------------------------------------------------------------------- /chapter5/public_rt.tf: -------------------------------------------------------------------------------- 1 | // Internet Gateway 2 | resource "aws_internet_gateway" "igw" { 3 | vpc_id = aws_vpc.management.id 4 | 5 | tags = { 6 | Name = "igw_${var.vpc_name}" 7 | Author = var.author 8 | } 9 | } 10 | 11 | // Public Route Table 12 | resource "aws_route_table" "public_rt" { 13 | vpc_id = aws_vpc.management.id 14 | 15 | route { 16 | cidr_block = "0.0.0.0/0" 17 | gateway_id = aws_internet_gateway.igw.id 18 | } 19 | 20 | tags = { 21 | Name = "public_rt_${var.vpc_name}" 22 | Author = var.author 23 | } 24 | } 25 | 26 | // Associate public subnets to public route table 27 | resource "aws_route_table_association" "public" { 28 | count = var.public_subnets_count 29 | subnet_id = element(aws_subnet.public_subnets.*.id, count.index) 30 | route_table_id = aws_route_table.public_rt.id 31 | } -------------------------------------------------------------------------------- /chapter5/route53.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_record" "jenkins_master" { 2 | zone_id = var.hosted_zone_id 3 | name = "jenkins.${var.domain_name}" 4 | type = "A" 5 | 6 | alias { 7 | name = aws_elb.jenkins_elb.dns_name 8 | zone_id = aws_elb.jenkins_elb.zone_id 9 | evaluate_target_health = true 10 | } 11 | } -------------------------------------------------------------------------------- /chapter5/subnets.tf: -------------------------------------------------------------------------------- 1 | // 2 Public Subnets 2 | resource "aws_subnet" "public_subnets" { 3 | vpc_id = aws_vpc.management.id 4 | cidr_block = "10.0.${count.index * 2 + 1}.0/24" 5 | availability_zone = element(var.availability_zones, count.index) 6 | map_public_ip_on_launch = true 7 | 8 | count = var.public_subnets_count 9 | 10 | tags = { 11 | Name = "public_10.0.${count.index * 2 + 1}.0_${element(var.availability_zones, count.index)}" 12 | Author = var.author 13 | } 14 | } 15 | 16 | // 2 Private Subnets 17 | resource "aws_subnet" "private_subnets" { 18 | vpc_id = aws_vpc.management.id 19 | cidr_block = "10.0.${count.index * 2}.0/24" 20 | availability_zone = element(var.availability_zones, count.index) 21 | map_public_ip_on_launch = false 22 | 23 | count = var.private_subnets_count 24 | 25 | tags = { 26 | Name = "private_10.0.${count.index * 2}.0_${element(var.availability_zones, count.index)}" 27 | Author = var.author 28 | } 29 | } -------------------------------------------------------------------------------- /chapter5/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter5/vpc.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "management" { 2 | cidr_block = var.cidr_block 3 | enable_dns_hostnames = true 4 | 5 | tags = { 6 | Name = var.vpc_name 7 | Author = var.author 8 | } 9 | } -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/basic-security.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.security.* 5 | 6 | def instance = Jenkins.getInstance() 7 | 8 | println "--> creating local user 'USERNAME'" 9 | 10 | def hudsonRealm = new HudsonPrivateSecurityRealm(false) 11 | hudsonRealm.createAccount('USERNAME','USERNAME') 12 | instance.setSecurityRealm(hudsonRealm) 13 | 14 | def strategy = new FullControlOnceLoggedInAuthorizationStrategy() 15 | instance.setAuthorizationStrategy(strategy) 16 | instance.save() -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/csrf-protection.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import hudson.security.csrf.DefaultCrumbIssuer 4 | import jenkins.model.Jenkins 5 | 6 | println "--> enabling CSRF protection" 7 | def instance = Jenkins.instance 8 | instance.setCrumbIssuer(new DefaultCrumbIssuer(true)) 9 | instance.save() -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/disable-cli.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | 5 | Jenkins jenkins = Jenkins.getInstance() 6 | 7 | println "--> disabling Jenkins remote CLI" 8 | jenkins.CLI.get().setEnabled(false) 9 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/disable-jnlp.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | import jenkins.security.s2m.* 5 | 6 | Jenkins jenkins = Jenkins.getInstance() 7 | 8 | println "--> disabling JNLP" 9 | jenkins.setSlaveAgentPort(-1) 10 | 11 | println "--> disabling non-encrypted protocols" 12 | HashSet newProtocols = new HashSet<>(jenkins.getAgentProtocols()); 13 | newProtocols.removeAll(Arrays.asList( 14 | "JNLP3-connect", "JNLP2-connect", "JNLP-connect", "CLI-connect" 15 | )); 16 | jenkins.setAgentProtocols(newProtocols); 17 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/node-agent.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.model.* 2 | import com.cloudbees.plugins.credentials.* 3 | import com.cloudbees.plugins.credentials.common.* 4 | import com.cloudbees.plugins.credentials.domains.* 5 | import com.cloudbees.plugins.credentials.impl.* 6 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.* 7 | import hudson.plugins.sshslaves.*; 8 | 9 | println "--> creating SSH credentials" 10 | 11 | domain = Domain.global() 12 | store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 13 | 14 | slavesPrivateKey = new BasicSSHUserPrivateKey( 15 | CredentialsScope.GLOBAL, 16 | "jenkins-slaves", 17 | "USERNAME", 18 | new BasicSSHUserPrivateKey.UsersPrivateKeySource(), 19 | "", 20 | "" 21 | ) 22 | 23 | store.addCredentials(domain, slavesPrivateKey) -------------------------------------------------------------------------------- /chapter6/azure/packer/master/scripts/skip-jenkins-setup.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.util.*; 5 | import jenkins.install.*; 6 | 7 | def instance = Jenkins.getInstance() 8 | 9 | instance.setInstallState(InstallState.INITIAL_SETUP_COMPLETED) -------------------------------------------------------------------------------- /chapter6/azure/packer/worker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java JDK 8" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | 7 | echo "Install Docker engine" 8 | yum install -y yum-utils device-mapper-persistent-data lvm2 9 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 10 | yum update -y 11 | yum install -y docker-ce-18.09.1 12 | systemctl start docker 13 | 14 | echo "Install git" 15 | yum install -y git jq -------------------------------------------------------------------------------- /chapter6/azure/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bastion" { 2 | value = azurerm_public_ip.bastion_public_ip.ip_address 3 | } 4 | 5 | output "jenkins" { 6 | value = azurerm_public_ip.jenkins_lb_public_ip.ip_address 7 | } -------------------------------------------------------------------------------- /chapter6/azure/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | version = "=1.44.0" 3 | 4 | subscription_id = var.subscription_id 5 | client_id = var.client_id 6 | client_secret = var.client_secret 7 | tenant_id = var.tenant_id 8 | } -------------------------------------------------------------------------------- /chapter6/azure/terraform/virtual_network.tf: -------------------------------------------------------------------------------- 1 | data "azurerm_resource_group" "management" { 2 | name = var.resource_group 3 | } 4 | 5 | resource "azurerm_virtual_network" "management" { 6 | name = "management" 7 | location = var.location 8 | resource_group_name = data.azurerm_resource_group.management.name 9 | address_space = [var.base_cidr_block] 10 | dns_servers = ["10.0.0.4", "10.0.0.5"] 11 | 12 | dynamic "subnet" { 13 | for_each = [for s in var.subnets: { 14 | name = s.name 15 | prefix = cidrsubnet(var.base_cidr_block, 8, s.number) 16 | }] 17 | 18 | content { 19 | name = subnet.value.name 20 | address_prefix = subnet.value.prefix 21 | } 22 | } 23 | 24 | subnet { 25 | name = "AzureBastionSubnet" 26 | address_prefix = cidrsubnet(var.base_cidr_block, 11, 224) 27 | } 28 | 29 | tags = { 30 | environment = "management" 31 | } 32 | } -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/basic-security.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.security.* 5 | 6 | def instance = Jenkins.getInstance() 7 | 8 | println "--> creating local user 'USERNAME'" 9 | 10 | def hudsonRealm = new HudsonPrivateSecurityRealm(false) 11 | hudsonRealm.createAccount('USERNAME','PASSWORD') 12 | instance.setSecurityRealm(hudsonRealm) 13 | 14 | def strategy = new FullControlOnceLoggedInAuthorizationStrategy() 15 | instance.setAuthorizationStrategy(strategy) 16 | instance.save() -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/csrf-protection.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import hudson.security.csrf.DefaultCrumbIssuer 4 | import jenkins.model.Jenkins 5 | 6 | println "--> enabling CSRF protection" 7 | def instance = Jenkins.instance 8 | instance.setCrumbIssuer(new DefaultCrumbIssuer(true)) 9 | instance.save() -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/disable-cli.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | 5 | Jenkins jenkins = Jenkins.getInstance() 6 | 7 | println "--> disabling Jenkins remote CLI" 8 | jenkins.CLI.get().setEnabled(false) 9 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/disable-jnlp.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | import jenkins.security.s2m.* 5 | 6 | Jenkins jenkins = Jenkins.getInstance() 7 | 8 | println "--> disabling JNLP" 9 | jenkins.setSlaveAgentPort(-1) 10 | 11 | println "--> disabling non-encrypted protocols" 12 | HashSet newProtocols = new HashSet<>(jenkins.getAgentProtocols()); 13 | newProtocols.removeAll(Arrays.asList( 14 | "JNLP3-connect", "JNLP2-connect", "JNLP-connect", "CLI-connect" 15 | )); 16 | jenkins.setAgentProtocols(newProtocols); 17 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/node-agent.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.model.* 2 | import com.cloudbees.plugins.credentials.* 3 | import com.cloudbees.plugins.credentials.common.* 4 | import com.cloudbees.plugins.credentials.domains.* 5 | import com.cloudbees.plugins.credentials.impl.* 6 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.* 7 | import hudson.plugins.sshslaves.*; 8 | 9 | println "--> creating SSH credentials" 10 | 11 | domain = Domain.global() 12 | store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 13 | 14 | slavesPrivateKey = new BasicSSHUserPrivateKey( 15 | CredentialsScope.GLOBAL, 16 | "jenkins-slaves", 17 | "USERNAME", 18 | new BasicSSHUserPrivateKey.UsersPrivateKeySource(), 19 | "", 20 | "" 21 | ) 22 | 23 | store.addCredentials(domain, slavesPrivateKey) -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/master/scripts/skip-jenkins-setup.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.util.*; 5 | import jenkins.install.*; 6 | 7 | def instance = Jenkins.getInstance() 8 | 9 | instance.setInstallState(InstallState.INITIAL_SETUP_COMPLETED) -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/worker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java JDK 8" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | 7 | echo "Install Docker engine" 8 | yum install -y yum-utils device-mapper-persistent-data lvm2 9 | yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 10 | yum update -y 11 | yum install -y docker-ce-18.09.1 12 | systemctl start docker 13 | 14 | echo "Install git" 15 | yum install -y git jq -------------------------------------------------------------------------------- /chapter6/digitalocean/packer/worker/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "api_token" : "d8dea550edff6a3273ef9014c44d0db1f3e2f531a78d6f913e312745136c2e19", 4 | "region": "lon1" 5 | }, 6 | "builders" : [ 7 | { 8 | "type": "digitalocean", 9 | "api_token": "{{user `api_token`}}", 10 | "image": "centos-8-x64", 11 | "region": "{{user `region`}}", 12 | "size": "512mb", 13 | "ssh_username": "root", 14 | "snapshot_name": "jenkins-worker" 15 | } 16 | ], 17 | "provisioners" : [ 18 | { 19 | "type" : "shell", 20 | "script" : "./setup.sh", 21 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /chapter6/digitalocean/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "master" { 2 | value = digitalocean_droplet.jenkins_master.ipv4_address 3 | } -------------------------------------------------------------------------------- /chapter6/digitalocean/terraform/scripts/join-cluster.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JENKINS_URL="${jenkins_url}" 4 | JENKINS_USERNAME="${jenkins_username}" 5 | JENKINS_PASSWORD="${jenkins_password}" 6 | TOKEN=$(curl -u $JENKINS_USERNAME:$JENKINS_PASSWORD ''$JENKINS_URL'/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)') 7 | INSTANCE_NAME=$(curl -s http://169.254.169.254/metadata/v1/hostname) 8 | INSTANCE_IP=$(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address) 9 | JENKINS_CREDENTIALS_ID="${jenkins_credentials_id}" 10 | 11 | curl -v -u $JENKINS_USERNAME:$JENKINS_PASSWORD -H "Jenkins-Crumb:$TOKEN" -d 'script= 12 | import hudson.model.Node.Mode 13 | import hudson.slaves.* 14 | import jenkins.model.Jenkins 15 | import hudson.plugins.sshslaves.SSHLauncher 16 | 17 | DumbSlave dumb = new DumbSlave("'$INSTANCE_NAME'", 18 | "'$INSTANCE_NAME'", 19 | "/home/root", 20 | "3", 21 | Mode.NORMAL, 22 | "workers", 23 | new SSHLauncher("'$INSTANCE_IP'", 22, "'$JENKINS_CREDENTIALS_ID'"), 24 | RetentionStrategy.INSTANCE) 25 | Jenkins.instance.addNode(dumb) 26 | ' $JENKINS_URL/script -------------------------------------------------------------------------------- /chapter6/digitalocean/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "digitalocean" { 2 | token = var.token 3 | } 4 | -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/basic-security.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.security.* 5 | 6 | def instance = Jenkins.getInstance() 7 | 8 | println "--> creating local user 'USERNAME'" 9 | 10 | def hudsonRealm = new HudsonPrivateSecurityRealm(false) 11 | hudsonRealm.createAccount('USERNAME','PASSWORD') 12 | instance.setSecurityRealm(hudsonRealm) 13 | 14 | def strategy = new FullControlOnceLoggedInAuthorizationStrategy() 15 | instance.setAuthorizationStrategy(strategy) 16 | instance.save() -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/csrf-protection.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import hudson.security.csrf.DefaultCrumbIssuer 4 | import jenkins.model.Jenkins 5 | 6 | println "--> enabling CSRF protection" 7 | def instance = Jenkins.instance 8 | instance.setCrumbIssuer(new DefaultCrumbIssuer(true)) 9 | instance.save() -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/disable-cli.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | 5 | Jenkins jenkins = Jenkins.getInstance() 6 | 7 | println "--> disabling Jenkins remote CLI" 8 | jenkins.CLI.get().setEnabled(false) 9 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/disable-jnlp.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.Jenkins 4 | import jenkins.security.s2m.* 5 | 6 | Jenkins jenkins = Jenkins.getInstance() 7 | 8 | println "--> disabling JNLP" 9 | jenkins.setSlaveAgentPort(-1) 10 | 11 | println "--> disabling non-encrypted protocols" 12 | HashSet newProtocols = new HashSet<>(jenkins.getAgentProtocols()); 13 | newProtocols.removeAll(Arrays.asList( 14 | "JNLP3-connect", "JNLP2-connect", "JNLP-connect", "CLI-connect" 15 | )); 16 | jenkins.setAgentProtocols(newProtocols); 17 | jenkins.save() -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/node-agent.groovy: -------------------------------------------------------------------------------- 1 | import jenkins.model.* 2 | import com.cloudbees.plugins.credentials.* 3 | import com.cloudbees.plugins.credentials.common.* 4 | import com.cloudbees.plugins.credentials.domains.* 5 | import com.cloudbees.plugins.credentials.impl.* 6 | import com.cloudbees.jenkins.plugins.sshcredentials.impl.* 7 | import hudson.plugins.sshslaves.*; 8 | 9 | println "--> creating SSH credentials" 10 | 11 | domain = Domain.global() 12 | store = Jenkins.instance.getExtensionList('com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].getStore() 13 | 14 | slavesPrivateKey = new BasicSSHUserPrivateKey( 15 | CredentialsScope.GLOBAL, 16 | "jenkins-slaves", 17 | "USERNAME", 18 | new BasicSSHUserPrivateKey.UsersPrivateKeySource(), 19 | "", 20 | "" 21 | ) 22 | 23 | store.addCredentials(domain, slavesPrivateKey) -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/scripts/skip-jenkins-setup.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import jenkins.model.* 4 | import hudson.util.*; 5 | import jenkins.install.*; 6 | 7 | def instance = Jenkins.getInstance() 8 | 9 | instance.setInstallState(InstallState.INITIAL_SETUP_COMPLETED) -------------------------------------------------------------------------------- /chapter6/gcp/packer/master/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Jenkins stable release" 4 | yum remove -y java 5 | yum install -y wget java-11-openjdk-devel 6 | wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo 7 | rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key 8 | yum install -y jenkins 9 | chkconfig jenkins on 10 | 11 | echo "Install git" 12 | yum install -y git 13 | 14 | echo "Setup SSH key" 15 | mkdir /var/lib/jenkins/.ssh 16 | touch /var/lib/jenkins/.ssh/known_hosts 17 | chown -R jenkins:jenkins /var/lib/jenkins/.ssh 18 | chmod 700 /var/lib/jenkins/.ssh 19 | mv /tmp/id_rsa /var/lib/jenkins/.ssh/id_rsa 20 | chmod 600 /var/lib/jenkins/.ssh/id_rsa 21 | chown -R jenkins:jenkins /var/lib/jenkins/.ssh/id_rsa 22 | 23 | echo "Configure Jenkins" 24 | mkdir -p /var/lib/jenkins/init.groovy.d 25 | mv /tmp/*.groovy /var/lib/jenkins/init.groovy.d/ 26 | mv /tmp/jenkins /etc/sysconfig/jenkins 27 | chmod +x /tmp/install-plugins.sh 28 | bash /tmp/install-plugins.sh 29 | service jenkins start -------------------------------------------------------------------------------- /chapter6/gcp/packer/worker/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java JDK 8" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | 7 | echo "Install Docker engine" 8 | yum update -y 9 | yum install docker -y 10 | usermod -aG docker ec2-user 11 | systemctl enable docker 12 | 13 | echo "Install git" 14 | yum install -y git -------------------------------------------------------------------------------- /chapter6/gcp/packer/worker/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "service_account" : "SERVICE ACCOUNT FILE", 4 | "project": "GCP PROJECT ID", 5 | "zone": "ZONE ID" 6 | }, 7 | "builders" : [ 8 | { 9 | "type": "googlecompute", 10 | "image_name" : "jenkins-worker", 11 | "account_file": "{{user `service_account`}}", 12 | "project_id": "{{user `project`}}", 13 | "source_image_family": "centos-8", 14 | "ssh_username": "packer", 15 | "zone": "{{user `zone`}}" 16 | } 17 | ], 18 | "provisioners" : [ 19 | { 20 | "type" : "shell", 21 | "script" : "./setup.sh", 22 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /chapter6/gcp/terraform/bastion.tf: -------------------------------------------------------------------------------- 1 | resource "google_compute_firewall" "allow_ssl_to_bastion" { 2 | project = var.project 3 | name = "allow-ssl-to-bastion" 4 | network = google_compute_network.management.self_link 5 | 6 | allow { 7 | protocol = "tcp" 8 | ports = ["22"] 9 | } 10 | 11 | source_ranges = ["0.0.0.0/0"] 12 | 13 | source_tags = ["bastion"] 14 | } 15 | 16 | resource "google_compute_address" "static" { 17 | name = "ipv4-address" 18 | } 19 | 20 | resource "google_compute_instance" "bastion" { 21 | project = var.project 22 | name = "bastion" 23 | machine_type = var.bastion_machine_type 24 | zone = var.zone 25 | 26 | tags = ["bastion"] 27 | 28 | boot_disk { 29 | initialize_params { 30 | image = var.bastion_machine_image 31 | } 32 | } 33 | 34 | network_interface { 35 | subnetwork = google_compute_subnetwork.public_subnets[0].self_link 36 | 37 | access_config { 38 | nat_ip = google_compute_address.static.address 39 | } 40 | } 41 | 42 | metadata = { 43 | ssh-keys = "${var.ssh_user}:${file(var.ssh_public_key)}" 44 | } 45 | } -------------------------------------------------------------------------------- /chapter6/gcp/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bastion" { 2 | value = "${google_compute_instance.bastion.network_interface.0.access_config.0.nat_ip }" 3 | } 4 | 5 | output "jenkins" { 6 | value = google_compute_forwarding_rule.jenkins_master_forwarding_rule.ip_address 7 | } -------------------------------------------------------------------------------- /chapter6/gcp/terraform/scripts/join-cluster.tpl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | JENKINS_URL="${jenkins_url}" 4 | JENKINS_USERNAME="${jenkins_username}" 5 | JENKINS_PASSWORD="${jenkins_password}" 6 | TOKEN=$(curl -u $JENKINS_USERNAME:$JENKINS_PASSWORD ''$JENKINS_URL'/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)') 7 | INSTANCE_NAME=$(curl -s metadata.google.internal/0.1/meta-data/hostname) 8 | INSTANCE_IP=$(curl -s metadata.google.internal/0.1/meta-data/network | jq -r '.networkInterface[0].ip') 9 | JENKINS_CREDENTIALS_ID="${jenkins_credentials_id}" 10 | 11 | curl -v -u $JENKINS_USERNAME:$JENKINS_PASSWORD -H "Jenkins-Crumb:$TOKEN" -d 'script= 12 | import hudson.model.Node.Mode 13 | import hudson.slaves.* 14 | import jenkins.model.Jenkins 15 | import hudson.plugins.sshslaves.SSHLauncher 16 | 17 | DumbSlave dumb = new DumbSlave("'$INSTANCE_NAME'", 18 | "'$INSTANCE_NAME'", 19 | "/home/ec2-user", 20 | "3", 21 | Mode.NORMAL, 22 | "workers", 23 | new SSHLauncher("'$INSTANCE_IP'", 22, "'$JENKINS_CREDENTIALS_ID'"), 24 | RetentionStrategy.INSTANCE) 25 | Jenkins.instance.addNode(dumb) 26 | ' $JENKINS_URL/script -------------------------------------------------------------------------------- /chapter6/gcp/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "google" { 2 | credentials = file(var.credentials_path) 3 | project = var.project 4 | region = var.region 5 | } 6 | 7 | provider "google-beta" { 8 | credentials = file(var.credentials_path) 9 | project = var.project 10 | region = var.region 11 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.3 2 | LABEL MAINTAINER mlabouardy 3 | 4 | WORKDIR /app 5 | 6 | COPY requirements.txt . 7 | 8 | RUN pip install -r requirements.txt 9 | 10 | COPY movies.json main.py ./ 11 | 12 | CMD python main.py -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM python:3.7.3 2 | 3 | WORKDIR /app 4 | 5 | RUN pip install unittest-xml-reporting 6 | 7 | COPY test_main.py . 8 | COPY movies.json . 9 | 10 | CMD python test_main.py -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def imageName = 'mlabouardy/movies-loader' 2 | def registry = 'https://registry.slowcoder.com' 3 | 4 | node('workers'){ 5 | stage('Checkout'){ 6 | checkout scm 7 | } 8 | 9 | stage('Unit Tests'){ 10 | def imageTest= docker.build("${imageName}-test", "-f Dockerfile.test .") 11 | sh "docker run --rm -v $PWD/reports:/app/reports ${imageName}-test" 12 | junit "$PWD/reports/*.xml" 13 | } 14 | 15 | stage('Build'){ 16 | docker.build(imageName) 17 | } 18 | 19 | stage('Push'){ 20 | docker.withRegistry(registry, 'registry') { 21 | docker.image(imageName).push(commitID()) 22 | 23 | if (env.BRANCH_NAME == 'develop') { 24 | docker.image(imageName).push('develop') 25 | } 26 | } 27 | } 28 | } 29 | 30 | def commitID() { 31 | sh 'git rev-parse HEAD > .git/commitID' 32 | def commitID = readFile('.git/commitID').trim() 33 | sh 'rm .git/commitID' 34 | commitID 35 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import os 4 | 5 | sqs = boto3.client('sqs', region_name=os.environ['AWS_REGION']) 6 | 7 | queue_url = os.environ['SQS_URL'] 8 | 9 | def sendMovie(movie): 10 | response = sqs.send_message( 11 | QueueUrl=queue_url, 12 | MessageBody=(json.dumps(movie))) 13 | return response['MessageId'] 14 | 15 | with open('movies.json') as json_file: 16 | data = json.load(json_file) 17 | for movie in data: 18 | print('Name: ' + movie['title']) 19 | message = sendMovie(movie) 20 | print('Message ID: ' + message) -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | pytest -------------------------------------------------------------------------------- /chapter7/microservices/movies-loader/test_main.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import json 3 | 4 | 5 | class TestJSONLoaderMethods(unittest.TestCase): 6 | movies = [] 7 | 8 | @classmethod 9 | def setUpClass(cls): 10 | with open('movies.json') as json_file: 11 | cls.movies = json.load(json_file) 12 | 13 | def test_rank(self): 14 | self.assertEqual(self.movies[0]['rank'], '1') 15 | 16 | def test_title(self): 17 | self.assertEqual(self.movies[0]['title'], 'The Shawshank Redemption') 18 | 19 | def test_id(self): 20 | self.assertEqual(self.movies[0]['id'], 'tt0111161') 21 | 22 | if __name__ == '__main__': 23 | unittest.main() -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.17.0 as builder 2 | ARG ENVIRONMENT 3 | ENV CHROME_BIN=chromium 4 | WORKDIR /app 5 | RUN apt-get update && apt-get install -y chromium 6 | COPY package-lock.json package.json . 7 | RUN npm i && npm i -g @angular/cli 8 | COPY . . 9 | RUN ng build -c $ENVIRONMENT 10 | 11 | FROM nginx:alpine 12 | RUN rm -rf /usr/share/nginx/html/* 13 | COPY --from=builder /app/dist /usr/share/nginx/html 14 | EXPOSE 80 15 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM node:14.0.0 2 | 3 | ENV CHROME_BIN=chromium 4 | 5 | WORKDIR /app 6 | 7 | RUN apt-get update && apt-get install -y chromium 8 | 9 | COPY package-lock.json . 10 | COPY package.json . 11 | 12 | RUN npm i && npm i -g @angular/cli 13 | 14 | COPY . . -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('marketplace app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText() { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=angular:movies-marketplace 2 | sonar.projectName=movies-marketplace 3 | sonar.projectVersion=1.0.0 4 | sonar.sourceEncoding=UTF-8 5 | sonar.sources=src 6 | sonar.exclusions=**/node_modules/**,**/*.spec.ts 7 | sonar.tests=src/app 8 | sonar.test.inclusions=**/*.spec.ts 9 | sonar.ts.tslint.configPath=tslint.json 10 | sonar.javascript.lcov.reportPaths=coverage/lcov.info -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/api.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ApiService } from './api.service'; 4 | 5 | describe('ApiService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ApiService = TestBed.get(ApiService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | import { DashboardComponent } from './dashboard/dashboard.component'; 4 | import { FavoritesComponent } from './favorites/favorites.component'; 5 | import { MovieComponent } from './movie/movie.component'; 6 | 7 | const routes: Routes = [ 8 | { 9 | path: 'dashboard', 10 | component: DashboardComponent, 11 | data: { title: 'Sign In - Komiser' } 12 | }, 13 | { 14 | path: 'favorites', 15 | component: FavoritesComponent, 16 | data: { title: 'Join - Komiser' } 17 | }, 18 | { 19 | path: 'movies/:id', 20 | component: MovieComponent, 21 | data: { title: 'Expired token - Komiser' } 22 | }, 23 | { path: '**', 24 | redirectTo: 'dashboard' 25 | } 26 | ]; 27 | 28 | @NgModule({ 29 | imports: [ 30 | RouterModule.forRoot(routes) 31 | ], 32 | exports: [RouterModule] 33 | }) 34 | export class AppRoutingModule { } 35 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-root', 5 | templateUrl: './app.component.html', 6 | styleUrls: ['./app.component.css'] 7 | }) 8 | export class AppComponent { 9 | title = 'marketplace'; 10 | } 11 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { HttpModule } from '@angular/http'; 4 | import { FormsModule } from '@angular/forms'; 5 | 6 | import { AppRoutingModule } from './app-routing.module'; 7 | import { AppComponent } from './app.component'; 8 | import { DashboardComponent } from './dashboard/dashboard.component'; 9 | import { FavoritesComponent } from './favorites/favorites.component'; 10 | import { MovieComponent } from './movie/movie.component'; 11 | 12 | import { ApiService } from './api.service'; 13 | 14 | @NgModule({ 15 | declarations: [ 16 | AppComponent, 17 | DashboardComponent, 18 | FavoritesComponent, 19 | MovieComponent 20 | ], 21 | imports: [ 22 | BrowserModule, 23 | AppRoutingModule, 24 | HttpModule, 25 | FormsModule 26 | ], 27 | providers: [ 28 | ApiService 29 | ], 30 | bootstrap: [AppComponent] 31 | }) 32 | export class AppModule { } 33 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | .card-text{ 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | height: 100px;; 5 | } 6 | 7 | .card-title{ 8 | height: 50px; 9 | font-size: 1.1rem; 10 | } 11 | 12 | .card{ 13 | margin-top: 5px; 14 | margin-bottom: 5px; 15 | } 16 | 17 | .search-btn{ 18 | margin-left: 5px; 19 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { DashboardComponent } from './dashboard.component'; 4 | 5 | describe('DashboardComponent', () => { 6 | let component: DashboardComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ DashboardComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(DashboardComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ApiService } from '../api.service'; 3 | 4 | @Component({ 5 | selector: 'app-dashboard', 6 | templateUrl: './dashboard.component.html', 7 | styleUrls: ['./dashboard.component.css'] 8 | }) 9 | export class DashboardComponent implements OnInit { 10 | private movies: Array = new Array(); 11 | public keyword: string; 12 | 13 | constructor(private apiService: ApiService) { 14 | this.getMovies(); 15 | } 16 | 17 | private getMovies(){ 18 | this.apiService.getMovies().subscribe(data => { 19 | this.movies = data; 20 | }, err => { 21 | this.movies = []; 22 | }); 23 | } 24 | 25 | ngOnInit() { 26 | } 27 | 28 | public searchMovie(){ 29 | this.apiService.getMovie(this.keyword).subscribe(data => { 30 | this.movies = []; 31 | if(Array.isArray(data)) 32 | this.movies = data; 33 | else 34 | this.movies.push(data); 35 | }, err => { 36 | this.movies = []; 37 | }) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/favorites/favorites.component.css: -------------------------------------------------------------------------------- 1 | .card-text{ 2 | overflow: hidden; 3 | text-overflow: ellipsis; 4 | height: 100px;; 5 | } 6 | 7 | .card-title{ 8 | height: 50px; 9 | font-size: 1.1rem; 10 | } 11 | 12 | .card{ 13 | margin-top: 5px; 14 | margin-bottom: 5px; 15 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/favorites/favorites.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { FavoritesComponent } from './favorites.component'; 4 | 5 | describe('FavoritesComponent', () => { 6 | let component: FavoritesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ FavoritesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(FavoritesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/favorites/favorites.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ApiService } from '../api.service'; 3 | 4 | @Component({ 5 | selector: 'app-favorites', 6 | templateUrl: './favorites.component.html', 7 | styleUrls: ['./favorites.component.css'] 8 | }) 9 | export class FavoritesComponent implements OnInit { 10 | private movies: Array = new Array(); 11 | 12 | constructor(private apiService: ApiService) { 13 | this.getMovies(); 14 | } 15 | 16 | private getMovies(){ 17 | this.apiService.getFavorites().subscribe(data => { 18 | this.movies = data; 19 | }, err => { 20 | this.movies = []; 21 | }); 22 | } 23 | 24 | ngOnInit() { 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/movie/movie.component.css: -------------------------------------------------------------------------------- 1 | .badge { 2 | margin-right: 3px; 3 | } 4 | 5 | .title{ 6 | font-weight: 900; 7 | margin-bottom: 10px; 8 | margin-top:10px; 9 | } 10 | 11 | .video{ 12 | margin-right: 10px; 13 | } 14 | 15 | .actions{ 16 | margin-bottom: 20px; 17 | } 18 | 19 | .details{ 20 | margin-bottom: 20px; 21 | } 22 | 23 | .similar{ 24 | margin-right: 5px; 25 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/app/movie/movie.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { MovieComponent } from './movie.component'; 4 | 5 | describe('MovieComponent', () => { 6 | let component: MovieComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ MovieComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(MovieComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter7/microservices/movies-marketplace/src/assets/.gitkeep -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter7/microservices/movies-marketplace/src/assets/images/logo.png -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/environments/environment.production.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | apiURL: '', 4 | }; 5 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/environments/environment.sandbox.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | apiURL: 'http://localhost:3000', 4 | }; 5 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/environments/environment.staging.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false, 3 | apiURL: '', 4 | }; 5 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | apiURL: '', 8 | }; -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlabouardy/pipeline-as-code-with-jenkins/e3264e037fd0152f5a2dd95b8d9acb112d5faf1b/chapter7/microservices/movies-marketplace/src/favicon.ico -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.ts" 13 | ], 14 | "exclude": [ 15 | "src/test.ts", 16 | "src/**/*.spec.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | }, 22 | "angularCompilerOptions": { 23 | "fullTemplateTypeCheck": true, 24 | "strictInjectionParameters": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-marketplace/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-parser/.dockerignore: -------------------------------------------------------------------------------- 1 | vendor -------------------------------------------------------------------------------- /chapter7/microservices/movies-parser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.5 2 | WORKDIR /go/src/github.com/mlabouardy/movies-parser 3 | COPY main.go go.mod . 4 | RUN go get -v 5 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app main.go 6 | 7 | FROM alpine:latest 8 | LABEL Maintainer mlabouardy 9 | RUN apk --no-cache add ca-certificates 10 | WORKDIR /root/ 11 | COPY --from=0 /go/src/github.com/mlabouardy/movies-parser/app . 12 | CMD ["./app"] -------------------------------------------------------------------------------- /chapter7/microservices/movies-parser/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.4 2 | 3 | ENV VERSION v1.0.22 4 | ENV GOCACHE /tmp 5 | WORKDIR /go/src/github/mlabouardy/movies-parser 6 | 7 | RUN wget https://github.com/sonatype-nexus-community/nancy/releases/download/$VERSION/nancy-linux.amd64-$VERSION -O nancy && \ 8 | chmod +x nancy && mv nancy /usr/local/bin/nancy 9 | RUN go get -u golang.org/x/lint/golint 10 | 11 | COPY . . 12 | 13 | RUN go get -v -------------------------------------------------------------------------------- /chapter7/microservices/movies-parser/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/PuerkitoBio/goquery" 30 | version = "1.5.1" 31 | 32 | [[constraint]] 33 | name = "github.com/aws/aws-sdk-go-v2" 34 | version = "0.20.0" 35 | 36 | [[constraint]] 37 | name = "go.mongodb.org/mongo-driver" 38 | version = "1.3.2" 39 | 40 | [prune] 41 | go-tests = true 42 | unused-packages = true -------------------------------------------------------------------------------- /chapter7/microservices/movies-parser/go.mod: -------------------------------------------------------------------------------- 1 | module movies-parser 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/PuerkitoBio/goquery v1.5.1 7 | github.com/aws/aws-sdk-go-v2 v0.20.0 8 | github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf // indirect 9 | github.com/klauspost/compress v1.10.4 // indirect 10 | go.mongodb.org/mongo-driver v1.5.1 11 | golang.org/x/crypto v0.1.0 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "mocha": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly" 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2018 15 | }, 16 | "rules": { 17 | } 18 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | .DS_Store -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.17.0 2 | 3 | WORKDIR /app 4 | 5 | COPY package-lock.json package.json . 6 | 7 | RUN npm i --only=prod 8 | 9 | COPY index.js dao.js ./ 10 | 11 | CMD npm start -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM node:14.17.0 2 | 3 | WORKDIR /app 4 | 5 | COPY package-lock.json package.json . 6 | 7 | RUN npm i 8 | 9 | COPY . . -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/README.md: -------------------------------------------------------------------------------- 1 | # movies-store 2 | Movies Store 3 | -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/dao copy.js: -------------------------------------------------------------------------------- 1 | const Mongoose = require('mongoose') 2 | 3 | const movieSchema = new Mongoose.Schema({ 4 | title: String, 5 | id: String, 6 | poster: String, 7 | releaseDate: String, 8 | rating: String, 9 | genre: String, 10 | description: String, 11 | videos: [String], 12 | similar: [ 13 | { 14 | title: String, 15 | poster: String, 16 | } 17 | ], 18 | actors: [ 19 | { 20 | picture: String, 21 | name: String, 22 | role: String 23 | } 24 | ], 25 | rank: String, 26 | }) 27 | 28 | module.exports = { 29 | init: () => Mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }), 30 | close: () => Mongoose.disconnect(), 31 | Movie: Mongoose.model('movie', movieSchema), 32 | Favorite: Mongoose.model('favorite', movieSchema), 33 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/dao.js: -------------------------------------------------------------------------------- 1 | const Mongoose = require('mongoose') 2 | 3 | const movieSchema = new Mongoose.Schema({ 4 | title: String, 5 | id: String, 6 | poster: String, 7 | releaseDate: String, 8 | rating: String, 9 | genre: String, 10 | description: String, 11 | videos: [String], 12 | similar: [ 13 | { 14 | title: String, 15 | poster: String, 16 | } 17 | ], 18 | actors: [ 19 | { 20 | picture: String, 21 | name: String, 22 | role: String 23 | } 24 | ], 25 | rank: String, 26 | }) 27 | 28 | module.exports = { 29 | init: () => Mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }), 30 | close: () => Mongoose.disconnect(), 31 | Movie: Mongoose.model('movie', movieSchema), 32 | Favorite: Mongoose.model('favorite', movieSchema), 33 | } -------------------------------------------------------------------------------- /chapter7/microservices/movies-store/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movies-store", 3 | "version": "1.0.0", 4 | "description": "Movies store's API", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "mocha ./test/*.spec.js", 9 | "lint": "eslint .", 10 | "coverage-text": "nyc --reporter=text mocha", 11 | "coverage-html": "nyc --reporter=html mocha" 12 | }, 13 | "keywords": [ 14 | "movies", 15 | "nodejs", 16 | "store" 17 | ], 18 | "author": "Mohamed Labouardy", 19 | "license": "MIT", 20 | "dependencies": { 21 | "body-parser": "^1.19.0", 22 | "cors": "^2.8.5", 23 | "express": "^4.17.1", 24 | "mongoose": "^5.9.9" 25 | }, 26 | "devDependencies": { 27 | "chai": "^4.2.0", 28 | "eslint": "^6.8.0", 29 | "mocha": "^7.1.1", 30 | "mongo-unit": "^2.0.1", 31 | "mongodb-memory-server": "^6.3.1", 32 | "nyc": "^15.0.1" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter7/webhook-forwarder/index.js: -------------------------------------------------------------------------------- 1 | const Request = require('request'); 2 | 3 | exports.handler = (event, context, callback) => { 4 | Request.post({ 5 | url: process.env.JENKINS_URL, 6 | method: "POST", 7 | headers: { 8 | "Content-Type": "application/json", 9 | "X-GitHub-Event": event.headers["X-GitHub-Event"] 10 | }, 11 | json: JSON.parse(event.body) 12 | }, (error, response, body) => { 13 | callback(null, { 14 | "statusCode": 200, 15 | "headers": { 16 | "content-type": "application/json" 17 | }, 18 | "body": "success", 19 | "isBase64Encoded": false 20 | }) 21 | }) 22 | }; -------------------------------------------------------------------------------- /chapter7/webhook-forwarder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-webhook", 3 | "version": "1.0.0", 4 | "description": "Lambda Github Webhook Triggered by API Gateway to send requests to Jenkins", 5 | "main": "index.js", 6 | "author": "Mohamed Labouardy", 7 | "license": "Manning", 8 | "dependencies": { 9 | "request": "^2.88.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter7/webhook-forwarder/terraform/lambda.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_role" "role" { 2 | name = "GitHubWebhookForwarderRole" 3 | 4 | assume_role_policy = < .git/commitID' 17 | def commitID = readFile('.git/commitID').trim() 18 | sh 'rm .git/commitID' 19 | commitID 20 | } -------------------------------------------------------------------------------- /chapter8/pipelines/movies-parser/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def imageName = 'mlabouardy/movies-parser' 2 | 3 | node('workers'){ 4 | stage('Checkout'){ 5 | checkout scm 6 | } 7 | 8 | def imageTest= docker.build("${imageName}-test", "-f Dockerfile.test .") 9 | 10 | stage('Pre-integration Tests'){ 11 | parallel( 12 | 'Quality Tests': { 13 | imageTest.inside{ 14 | sh 'golint' 15 | } 16 | }, 17 | 'Unit Tests': { 18 | imageTest.inside{ 19 | sh 'go test' 20 | } 21 | }, 22 | 'Security Tests': { 23 | imageTest.inside('-u root:root'){ 24 | sh 'nancy /go/src/github/mlabouardy/movies-parser/Gopkg.lock' 25 | } 26 | } 27 | ) 28 | } 29 | } 30 | 31 | def commitID() { 32 | sh 'git rev-parse HEAD > .git/commitID' 33 | def commitID = readFile('.git/commitID').trim() 34 | sh 'rm .git/commitID' 35 | commitID 36 | } -------------------------------------------------------------------------------- /chapter8/services/outputs.tf: -------------------------------------------------------------------------------- 1 | output "visualizer" { 2 | value = "https://${aws_route53_record.visualizer.name}" 3 | } 4 | 5 | output "store" { 6 | value = "https://${aws_route53_record.movies_store.name}" 7 | } 8 | 9 | output "marketplace" { 10 | value = "https://${aws_route53_record.movies_marketplace.name}" 11 | } -------------------------------------------------------------------------------- /chapter8/services/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter8/sonar-scanner/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Install Java JDK 8" 4 | yum remove -y java 5 | yum install -y java-1.8.0-openjdk 6 | 7 | echo "Install Docker engine" 8 | yum update -y 9 | yum install docker -y 10 | usermod -aG docker ec2-user 11 | service docker start 12 | 13 | echo "Install git" 14 | yum install -y git 15 | 16 | echo "Install SonarScanner" 17 | wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-4.2.0.1873-linux.zip -P /tmp 18 | unzip /tmp/sonar-scanner-cli-4.2.0.1873-linux.zip 19 | mv sonar-scanner-4.2.0.1873-linux sonar-scanner 20 | ln -sf /home/ec2-user/sonar-scanner/bin/sonar-scanner /usr/bin/sonar-scanner -------------------------------------------------------------------------------- /chapter8/sonar-scanner/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "AWS REGION", 4 | "aws_profile": "AWS PROFILE", 5 | "source_ami" : "AMAZON LINUX AMI 2 ID", 6 | "instance_type": "INSTANCE TYPE" 7 | }, 8 | "builders" : [ 9 | { 10 | "type" : "amazon-ebs", 11 | "profile" : "{{user `aws_profile`}}", 12 | "region" : "{{user `region`}}", 13 | "instance_type" : "{{user `instance_type`}}", 14 | "source_ami" : "{{user `source_ami`}}", 15 | "ssh_username" : "ec2-user", 16 | "ami_name" : "jenkins-worker1", 17 | "ami_description" : "Jenkins worker's AMI with Sonar Scanner", 18 | "run_tags" : { 19 | "Name" : "packer-builder" 20 | } 21 | } 22 | ], 23 | "provisioners" : [ 24 | { 25 | "type" : "shell", 26 | "script" : "./setup.sh", 27 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /chapter8/sonarqube/packer/sonar.init.d: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # rc file for SonarQube 4 | # 5 | # chkconfig: 345 96 10 6 | # description: SonarQube system (www.sonarsource.org) 7 | # 8 | ### BEGIN INIT INFO 9 | # Provides: sonar 10 | # Required-Start: $network 11 | # Required-Stop: $network 12 | # Default-Start: 3 4 5 13 | # Default-Stop: 0 1 2 6 14 | # Short-Description: SonarQube system (www.sonarsource.org) 15 | # Description: SonarQube system (www.sonarsource.org) 16 | ### END INIT INFO 17 | 18 | /usr/bin/sonar $* -------------------------------------------------------------------------------- /chapter8/sonarqube/packer/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "region": "AWS REGION", 4 | "aws_profile": "AWS PROFILE ID", 5 | "source_ami": "Amazon Linux AMI 2 ID", 6 | "instance_type": "EC2 INSTANCE TYPE" 7 | }, 8 | "builders": [ 9 | { 10 | "type": "amazon-ebs", 11 | "profile": "{{user `aws_profile`}}", 12 | "region": "{{user `region`}}", 13 | "instance_type": "{{user `instance_type`}}", 14 | "source_ami": "{{user `source_ami`}}", 15 | "ssh_username": "ec2-user", 16 | "ami_name": "sonarqube-8.2.0.32929", 17 | "ami_description": "SonarQube community edition" 18 | } 19 | ], 20 | "provisioners": [ 21 | { 22 | "type": "file", 23 | "source": "sonar.init.d", 24 | "destination": "/tmp/" 25 | }, 26 | { 27 | "type": "shell", 28 | "script": "./setup.sh", 29 | "execute_command": "sudo -E -S sh '{{ .Path }}'" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /chapter8/sonarqube/terraform/dns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_record" "sonarqube" { 2 | zone_id = var.hosted_zone_id 3 | name = "sonarqube.${var.domain_name}" 4 | type = "A" 5 | 6 | alias { 7 | name = aws_elb.sonarqube_elb.dns_name 8 | zone_id = aws_elb.sonarqube_elb.zone_id 9 | evaluate_target_health = true 10 | } 11 | } -------------------------------------------------------------------------------- /chapter8/sonarqube/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "sonarqube" { 2 | value = "https://${aws_route53_record.sonarqube.name}" 3 | } -------------------------------------------------------------------------------- /chapter8/sonarqube/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter9/nexus/packer/nexus.rc: -------------------------------------------------------------------------------- 1 | run_as_user="nexus" -------------------------------------------------------------------------------- /chapter9/nexus/packer/repository.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docker-repository", 3 | "type": "groovy", 4 | "content": "import org.sonatype.nexus.blobstore.api.BlobStoreManager; import org.sonatype.nexus.repository.storage.WritePolicy; repository.createDockerHosted('docker-registry', 5000, 443, BlobStoreManager.DEFAULT_BLOBSTORE_NAME, true, true, WritePolicy.ALLOW, true)" 5 | } -------------------------------------------------------------------------------- /chapter9/nexus/terraform/dns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route53_record" "nexus" { 2 | zone_id = var.hosted_zone_id 3 | name = "nexus.${var.domain_name}" 4 | type = "A" 5 | 6 | alias { 7 | name = aws_elb.nexus_elb.dns_name 8 | zone_id = aws_elb.nexus_elb.zone_id 9 | evaluate_target_health = true 10 | } 11 | } 12 | 13 | resource "aws_route53_record" "registry" { 14 | zone_id = var.hosted_zone_id 15 | name = "registry.${var.domain_name}" 16 | type = "A" 17 | 18 | alias { 19 | name = aws_elb.registry_elb.dns_name 20 | zone_id = aws_elb.registry_elb.zone_id 21 | evaluate_target_health = true 22 | } 23 | } -------------------------------------------------------------------------------- /chapter9/nexus/terraform/nexus.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "nexus" { 2 | most_recent = true 3 | owners = ["self"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["nexus-*"] 8 | } 9 | } 10 | 11 | resource "aws_instance" "nexus" { 12 | ami = data.aws_ami.nexus.id 13 | instance_type = var.nexus_instance_type 14 | key_name = aws_key_pair.management.id 15 | vpc_security_group_ids = [aws_security_group.nexus_sg.id] 16 | subnet_id = element(aws_subnet.private_subnets, 0).id 17 | 18 | root_block_device { 19 | volume_type = "gp2" 20 | volume_size = 50 21 | delete_on_termination = false 22 | } 23 | 24 | tags = { 25 | Author = var.author 26 | Name = "nexus" 27 | } 28 | } -------------------------------------------------------------------------------- /chapter9/nexus/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "nexus" { 2 | value = "https://${aws_route53_record.nexus.name}" 3 | } 4 | 5 | output "registry" { 6 | value = "https://${aws_route53_record.registry.name}" 7 | } -------------------------------------------------------------------------------- /chapter9/nexus/terraform/terraform.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = var.region 3 | shared_credentials_file = var.shared_credentials_file 4 | profile = var.aws_profile 5 | } -------------------------------------------------------------------------------- /chapter9/pipelines/movies-loader/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7.3 2 | LABEL MAINTAINER mlabouardy 3 | 4 | WORKDIR /app 5 | 6 | COPY requirements.txt . 7 | 8 | RUN pip install -r requirements.txt 9 | 10 | COPY movies.json main.py ./ 11 | 12 | CMD python main.py -------------------------------------------------------------------------------- /chapter9/pipelines/movies-marketplace/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.17.0 as builder 2 | ARG ENVIRONMENT 3 | ENV CHROME_BIN=chromium 4 | WORKDIR /app 5 | RUN apt-get update && apt-get install -y chromium 6 | COPY package-lock.json package.json . 7 | RUN npm i && npm i -g @angular/cli 8 | COPY . . 9 | RUN ng build -c $ENVIRONMENT 10 | 11 | FROM nginx:alpine 12 | RUN rm -rf /usr/share/nginx/html/* 13 | COPY --from=builder /app/dist /usr/share/nginx/html 14 | EXPOSE 80 15 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /chapter9/pipelines/movies-parser/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.16.5 2 | WORKDIR /go/src/github.com/mlabouardy/movies-parser 3 | COPY main.go go.mod . 4 | RUN go get -v 5 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app main.go 6 | 7 | FROM alpine:latest 8 | LABEL Maintainer mlabouardy 9 | RUN apk --no-cache add ca-certificates 10 | WORKDIR /root/ 11 | COPY --from=0 /go/src/github.com/mlabouardy/movies-parser/app . 12 | CMD ["./app"] -------------------------------------------------------------------------------- /chapter9/pipelines/movies-store/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.17.0 2 | LABEL MAINTAINER mlabouardy 3 | 4 | WORKDIR /app 5 | 6 | COPY package-lock.json package.json . 7 | 8 | RUN npm i --only=prod 9 | 10 | COPY index.js dao.js ./ 11 | 12 | CMD npm start --------------------------------------------------------------------------------