├── .gitignore ├── README.md ├── build-a-docker-swarm-cluster-on-aws ├── packer │ └── docker │ │ ├── docker │ │ ├── setup.sh │ │ ├── telegraf.conf │ │ └── template.json └── terraform │ ├── ami.tf │ ├── elb.tf │ ├── iam.tf │ ├── outputs.tf │ ├── route53.tf │ ├── s3.tf │ ├── scripts │ └── setup-swarm.tpl │ ├── security_groups.tf │ ├── swarm-managers.tf │ ├── swarm-workers.tf │ ├── terraform.tf │ └── variables.tf ├── build-an-aws-vpc-with-infrastucture-as-code ├── packer │ └── bastion │ │ ├── setup.sh │ │ ├── telegraf.conf │ │ └── template.json └── terraform │ ├── ami.tf │ ├── bastion.tf │ ├── elb.tf │ ├── key_pair.tf │ ├── outputs.tf │ ├── route53.tf │ ├── security_groups.tf │ ├── subnets.tf │ ├── terraform.tf │ ├── variables.tf │ └── vpc.tf ├── core-concepts └── README.md ├── deploy-a-highly-available-jenkins-cluster-on-aws ├── packer │ ├── jenkins-master │ │ ├── basic-security.groovy │ │ ├── csrf-protection.groovy │ │ ├── disable-cli.groovy │ │ ├── disable-jnlp.groovy │ │ ├── install-plugins.sh │ │ ├── jenkins │ │ ├── jenkins.install.UpgradeWizard.state │ │ ├── node-agent.groovy │ │ ├── plugins.txt │ │ ├── setup.sh │ │ ├── telegraf.conf │ │ └── template.json │ └── jenkins-slave │ │ ├── setup.sh │ │ ├── telegraf.conf │ │ └── template.json └── terraform │ ├── ami.tf │ ├── elb.tf │ ├── iam.tf │ ├── jenkins-master.tf │ ├── jenkins-slaves.tf │ ├── outputs.tf │ ├── route53.tf │ ├── scripts │ └── join-cluster.tpl │ ├── security_groups.tf │ ├── terraform.tf │ └── variables.tf ├── functions.png ├── implement-a-cicd-pipeline-for-dockerized-microservices ├── application │ ├── api │ │ ├── config.js │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json │ ├── frontend │ │ ├── gulpfile.js │ │ ├── index.html │ │ ├── package-lock.json │ │ └── package.json │ └── worker │ │ └── main.go └── terraform │ ├── dynamodb.tf │ ├── output.tf │ ├── s3.tf │ ├── sqs.tf │ ├── terraform.tf │ └── variables.tf ├── manage-a-private-docker-registry-with-sonatype-nexus ├── packer │ └── nexus │ │ ├── nexus.rc │ │ ├── repository.json │ │ ├── setup.sh │ │ └── template.json └── terraform │ ├── ami.tf │ ├── elb.tf │ ├── jenkins-master.tf │ ├── jenkins-slaves.tf │ ├── nexus.tf │ ├── outputs.tf │ ├── route53.tf │ ├── scripts │ └── join-cluster.tpl │ ├── security_groups.tf │ ├── terraform.tf │ └── variables.tf └── microservices.png /.gitignore: -------------------------------------------------------------------------------- 1 | .terraform 2 | *.tfstate 3 | variables.tfvars 4 | .DS_Store 5 | node_modules/ 6 | build/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CI/CD for Microservices and Serverless Functions in AWS 2 | 3 | ## Learning Objectives 4 | 5 | * Core Concepts [[YouTube](https://www.youtube.com/watch?v=eAvxtPQVtDA)] [[Slides](https://fr.slideshare.net/medfreaky/cicd-for-microservices-and-serverless-functions-in-aws-core-concepts)] 6 | * Build an AWS VPC using Infrastructure as Code [[YouTube](https://www.youtube.com/watch?v=1tD5moDGKHM)] [[Slides](https://fr.slideshare.net/medfreaky/cicd-for-microservices-and-serverless-functions-in-aws-build-an-aws-vpc-using-infrastructure-as-code)] 7 | * Deploy a Highly available Jenkins cluster on AWS [[YouTube](https://www.youtube.com/watch?v=XCyiYoOZayI)] [[Slides](https://fr.slideshare.net/medfreaky/deploy-a-highly-available-jenkins-cluster-on-aws)] 8 | * Manage a Private Docker Registry with Sonatype Nexus [[YouTube](https://www.youtube.com/watch?v=YqswOqE-kD4)] [[Slides](https://fr.slideshare.net/medfreaky/manage-a-secure-private-docker-registry-with-sonatype-nexus-and-acm)] 9 | * Build a Docker Swarm cluster on AWS [[Slides](https://fr.slideshare.net/medfreaky/build-a-docker-swarm-cluster-on-aws)] 10 | * Implement a CI/CD Pipeline for Containerized Microservices [[Slides](https://fr.slideshare.net/medfreaky/build-a-cicd-pipeline-for-dockerized-microservices-in-aws)] 11 | * Implement a CI/CD Pipeline for Serverless Functions 12 | * Deployment Strategy 13 | * Integrate Security and Compliance into your CI/CD 14 | * Build a centralised monitoring and logging platform in AWS 15 | * Wrap up and going further 16 | 17 | 18 | ## CI/CD for Dockerized Microservices 19 | 20 |

21 | 22 |

23 | 24 | ## CI/CD for Serverless Functions 25 | 26 |

27 | 28 |

29 | -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/packer/docker/docker: -------------------------------------------------------------------------------- 1 | # The max number of open files for the daemon itself, and all 2 | # running containers. The default value of 1048576 mirrors the value 3 | # used by the systemd service unit. 4 | DAEMON_MAXFILES=1048576 5 | 6 | # Additional startup options for the Docker daemon, for example: 7 | # OPTIONS="--ip-forward=true --iptables=true" 8 | # By default we limit the number of open files per container 9 | OPTIONS="--default-ulimit nofile=1024:4096 -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock" 10 | 11 | # How many seconds the sysvinit script waits for the pidfile to appear 12 | # when starting the daemon. 13 | DAEMON_PIDFILE_TIMEOUT=10 -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/packer/docker/setup.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | echo "Install Telegraf" 4 | wget https://dl.influxdata.com/telegraf/releases/telegraf-1.6.0-1.x86_64.rpm -O /tmp/telegraf.rpm 5 | yum localinstall -y /tmp/telegraf.rpm 6 | rm /tmp/telegraf.rpm 7 | chkconfig telegraf on 8 | usermod -aG docker telegraf 9 | mv /tmp/telegraf.conf /etc/telegraf/telegraf.conf 10 | service telegraf start 11 | 12 | echo "Install Docker CE" 13 | yum update -y 14 | yum install docker -y 15 | service docker start 16 | usermod -aG docker ec2-user 17 | mv /tmp/docker /etc/sysconfig/docker 18 | chmod 644 /etc/sysconfig/docker 19 | service docker restart -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/packer/docker/telegraf.conf: -------------------------------------------------------------------------------- 1 | [global_tags] 2 | hostname="Docker" 3 | tool="Packer" 4 | 5 | # Read metrics about CPU usage 6 | [[inputs.cpu]] 7 | percpu = false 8 | totalcpu = true 9 | fieldpass = [ "usage*" ] 10 | name_suffix = "_vm" 11 | 12 | # Read metrics about disk usagee 13 | [[inputs.disk]] 14 | fielddrop = [ "inodes*" ] 15 | mount_points=["/"] 16 | name_suffix = "_vm" 17 | 18 | # Read metrics about network usage 19 | [[inputs.net]] 20 | interfaces = [ "eth0", "eth1" ] 21 | fielddrop = [ "icmp*", "ip*", "tcp*", "udp*" ] 22 | name_suffix = "_vm" 23 | 24 | # Read metrics about memory usage 25 | [[inputs.mem]] 26 | name_suffix = "_vm" 27 | 28 | # Read metrics about swap memory usage 29 | [[inputs.swap]] 30 | name_suffix = "_vm" 31 | 32 | # Read metrics about system load & uptime 33 | [[inputs.system]] 34 | name_suffix = "_vm" 35 | 36 | # Read metrics from docker socket api 37 | [[inputs.docker]] 38 | endpoint = "unix:///var/run/docker.sock" 39 | container_names = [] 40 | name_suffix = "_docker" 41 | 42 | [[outputs.influxdb]] 43 | database = "instances" 44 | urls = ["http://10.0.0.10:8086"] 45 | namepass = ["*_vm"] 46 | 47 | [[outputs.influxdb]] 48 | database = "containers" 49 | urls = ["http://10.0.0.10:8086"] 50 | namepass = ["*_docker"] -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/packer/docker/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "eu-west-2", 4 | "source_ami" : "ami-01419b804382064e4" 5 | }, 6 | "builders" : [ 7 | { 8 | "type" : "amazon-ebs", 9 | "profile" : "default", 10 | "region" : "{{user `region`}}", 11 | "instance_type" : "t2.micro", 12 | "source_ami" : "{{user `source_ami`}}", 13 | "ssh_username" : "ec2-user", 14 | "ami_name" : "docker-18.06.1-ce", 15 | "ami_description" : "Amazon Linux Image with Docker-CE", 16 | "run_tags" : { 17 | "Name" : "packer-builder-docker" 18 | }, 19 | "tags" : { 20 | "Tool" : "Packer", 21 | "Author" : "mlabouardy" 22 | } 23 | } 24 | ], 25 | "provisioners" : [ 26 | { 27 | "type" : "file", 28 | "source" : "./docker", 29 | "destination" : "/tmp/docker" 30 | }, 31 | { 32 | "type" : "file", 33 | "source" : "./telegraf.conf", 34 | "destination" : "/tmp/telegraf.conf" 35 | }, 36 | { 37 | "type" : "shell", 38 | "script" : "./setup.sh", 39 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/terraform/ami.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "docker" { 2 | most_recent = true 3 | owners = ["self"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["docker-18.06.1-ce"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/terraform/elb.tf: -------------------------------------------------------------------------------- 1 | // Cluster Visualizer ELB 2 | resource "aws_elb" "swarm_viz_elb" { 3 | subnets = ["${var.vpc_public_subnets}"] 4 | cross_zone_load_balancing = true 5 | security_groups = ["${aws_security_group.elb_viz_sg.id}"] 6 | 7 | listener { 8 | instance_port = 8080 9 | instance_protocol = "http" 10 | lb_port = 443 11 | lb_protocol = "https" 12 | ssl_certificate_id = "${var.ssl_arn}" 13 | } 14 | 15 | health_check { 16 | healthy_threshold = 2 17 | unhealthy_threshold = 2 18 | timeout = 3 19 | target = "HTTP:8080/" 20 | interval = 5 21 | } 22 | 23 | tags { 24 | Name = "elb_viz_${var.environment}" 25 | Author = "mlabouardy" 26 | Tool = "Terraform" 27 | Environment = "${var.environment}" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /build-a-docker-swarm-cluster-on-aws/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_instance_profile" "profile" { 2 | name = "SwarmDiscoveryBucketAccess" 3 | role = "${aws_iam_role.role.name}" 4 | } 5 | 6 | resource "aws_iam_role" "role" { 7 | name = "SwarmDiscoveryBucketRole" 8 | path = "/" 9 | 10 | assume_role_policy = < 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() -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/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() -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/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() -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/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() 18 | -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/install-plugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | plugin_dir=/var/lib/jenkins/plugins 6 | file_owner=jenkins.jenkins 7 | 8 | mkdir -p /var/lib/jenkins/plugins 9 | 10 | installPlugin() { 11 | if [ -f ${plugin_dir}/${1}.hpi -o -f ${plugin_dir}/${1}.jpi ]; then 12 | if [ "$2" == "1" ]; then 13 | return 1 14 | fi 15 | echo "Skipped: $1 (already installed)" 16 | return 0 17 | else 18 | echo "Installing: $1" 19 | curl -L --silent --output ${plugin_dir}/${1}.hpi https://updates.jenkins-ci.org/latest/${1}.hpi 20 | return 0 21 | fi 22 | } 23 | 24 | while read -r plugin 25 | do 26 | installPlugin "$plugin" 27 | done < "/tmp/plugins.txt" 28 | 29 | changed=1 30 | maxloops=100 31 | 32 | while [ "$changed" == "1" ]; do 33 | echo "Check for missing dependecies ..." 34 | if [ $maxloops -lt 1 ] ; then 35 | echo "Max loop count reached - probably a bug in this script: $0" 36 | exit 1 37 | fi 38 | ((maxloops--)) 39 | changed=0 40 | for f in ${plugin_dir}/*.hpi ; do 41 | deps=$( unzip -p ${f} META-INF/MANIFEST.MF | tr -d '\r' | sed -e ':a;N;$!ba;s/\n //g' | grep -e "^Plugin-Dependencies: " | awk '{ print $2 }' | tr ',' '\n' | awk -F ':' '{ print $1 }' | tr '\n' ' ' ) 42 | for plugin in $deps; do 43 | installPlugin "$plugin" 1 && changed=1 44 | done 45 | done 46 | done 47 | 48 | echo "fixing permissions" 49 | 50 | chown ${file_owner} ${plugin_dir} -R 51 | 52 | echo "all done" -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/jenkins: -------------------------------------------------------------------------------- 1 | ## Path: Development/Jenkins 2 | ## Description: Jenkins Automation Server 3 | ## Type: string 4 | ## Default: "/var/lib/jenkins" 5 | ## ServiceRestart: jenkins 6 | # 7 | # Directory where Jenkins store its configuration and working 8 | # files (checkouts, build reports, artifacts, ...). 9 | # 10 | JENKINS_HOME="/var/lib/jenkins" 11 | 12 | ## Type: string 13 | ## Default: "" 14 | ## ServiceRestart: jenkins 15 | # 16 | # Java executable to run Jenkins 17 | # When left empty, we'll try to find the suitable Java. 18 | # 19 | JENKINS_JAVA_CMD="" 20 | 21 | ## Type: string 22 | ## Default: "jenkins" 23 | ## ServiceRestart: jenkins 24 | # 25 | # Unix user account that runs the Jenkins daemon 26 | # Be careful when you change this, as you need to update 27 | # permissions of $JENKINS_HOME and /var/log/jenkins. 28 | # 29 | JENKINS_USER="jenkins" 30 | 31 | ## Type: string 32 | ## Default: "false" 33 | ## ServiceRestart: jenkins 34 | # 35 | # Whether to skip potentially long-running chown at the 36 | # $JENKINS_HOME location. Do not enable this, "true", unless 37 | # you know what you're doing. See JENKINS-23273. 38 | # 39 | #JENKINS_INSTALL_SKIP_CHOWN="false" 40 | 41 | ## Type: string 42 | ## Default: "-Djava.awt.headless=true" 43 | ## ServiceRestart: jenkins 44 | # 45 | # Options to pass to java when running Jenkins. 46 | # 47 | JENKINS_JAVA_OPTIONS="-Djava.awt.headless=true" 48 | 49 | ## Type: integer(0:65535) 50 | ## Default: 8080 51 | ## ServiceRestart: jenkins 52 | # 53 | # Port Jenkins is listening on. 54 | # Set to -1 to disable 55 | # 56 | JENKINS_PORT="8080" 57 | 58 | ## Type: string 59 | ## Default: "" 60 | ## ServiceRestart: jenkins 61 | # 62 | # IP address Jenkins listens on for HTTP requests. 63 | # Default is all interfaces (0.0.0.0). 64 | # 65 | JENKINS_LISTEN_ADDRESS="0.0.0.0" 66 | 67 | ## Type: integer(0:65535) 68 | ## Default: "" 69 | ## ServiceRestart: jenkins 70 | # 71 | # HTTPS port Jenkins is listening on. 72 | # Default is disabled. 73 | # 74 | JENKINS_HTTPS_PORT="" 75 | 76 | ## Type: string 77 | ## Default: "" 78 | ## ServiceRestart: jenkins 79 | # 80 | # Path to the keystore in JKS format (as created by the JDK 'keytool'). 81 | # Default is disabled. 82 | # 83 | JENKINS_HTTPS_KEYSTORE="" 84 | 85 | ## Type: string 86 | ## Default: "" 87 | ## ServiceRestart: jenkins 88 | # 89 | # Password to access the keystore defined in JENKINS_HTTPS_KEYSTORE. 90 | # Default is disabled. 91 | # 92 | JENKINS_HTTPS_KEYSTORE_PASSWORD="" 93 | 94 | ## Type: string 95 | ## Default: "" 96 | ## ServiceRestart: jenkins 97 | # 98 | # IP address Jenkins listens on for HTTPS requests. 99 | # Default is disabled. 100 | # 101 | JENKINS_HTTPS_LISTEN_ADDRESS="" 102 | 103 | 104 | ## Type: integer(1:9) 105 | ## Default: 5 106 | ## ServiceRestart: jenkins 107 | # 108 | # Debug level for logs -- the higher the value, the more verbose. 109 | # 5 is INFO. 110 | # 111 | JENKINS_DEBUG_LEVEL="5" 112 | 113 | ## Type: yesno 114 | ## Default: no 115 | ## ServiceRestart: jenkins 116 | # 117 | # Whether to enable access logging or not. 118 | # 119 | JENKINS_ENABLE_ACCESS_LOG="no" 120 | 121 | ## Type: integer 122 | ## Default: 100 123 | ## ServiceRestart: jenkins 124 | # 125 | # Maximum number of HTTP worker threads. 126 | # 127 | JENKINS_HANDLER_MAX="100" 128 | 129 | ## Type: integer 130 | ## Default: 20 131 | ## ServiceRestart: jenkins 132 | # 133 | # Maximum number of idle HTTP worker threads. 134 | # 135 | JENKINS_HANDLER_IDLE="20" 136 | 137 | ## Type: string 138 | ## Default: "" 139 | ## ServiceRestart: jenkins 140 | # 141 | # Pass arbitrary arguments to Jenkins. 142 | # Full option list: java -jar jenkins.war --help 143 | # 144 | JENKINS_ARGS="" -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/jenkins.install.UpgradeWizard.state: -------------------------------------------------------------------------------- 1 | 2.150.1 -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/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 | privateKey = new File('/tmp/id_rsa').getText('UTF-8') 15 | 16 | slavesPrivateKey = new BasicSSHUserPrivateKey( 17 | CredentialsScope.GLOBAL, 18 | "jenkins-slaves", 19 | "ec2-user", 20 | new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(privateKey), 21 | "", 22 | "" 23 | ) 24 | 25 | managersPrivateKey = new BasicSSHUserPrivateKey( 26 | CredentialsScope.GLOBAL, 27 | "swarm-managers", 28 | "ec2-user", 29 | new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(privateKey), 30 | "", 31 | "" 32 | ) 33 | 34 | githubCredentials = new UsernamePasswordCredentialsImpl( 35 | CredentialsScope.GLOBAL, 36 | "github", "Github credentials", 37 | "USERNAME", 38 | "PASSWORD" 39 | ) 40 | 41 | registryCredentials = new UsernamePasswordCredentialsImpl( 42 | CredentialsScope.GLOBAL, 43 | "registry", "Docker Registry credentials", 44 | "USERNAME", 45 | "PASSWORD" 46 | ) 47 | 48 | store.addCredentials(domain, slavesPrivateKey) 49 | store.addCredentials(domain, managersPrivateKey) 50 | store.addCredentials(domain, githubCredentials) 51 | store.addCredentials(domain, registryCredentials) -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/plugins.txt: -------------------------------------------------------------------------------- 1 | ace-editor 2 | amazon-ecr 3 | ant 4 | antisamy-markup-formatter 5 | apache-httpcomponents-client-4-api 6 | authentication-tokens 7 | aws-credentials 8 | aws-java-sdk 9 | blueocean 10 | blueocean-autofavorite 11 | blueocean-bitbucket-pipeline 12 | blueocean-commons 13 | blueocean-configs 14 | blueocean-config-js 15 | blueocean-dashboard 16 | blueocean-display-url 17 | blueocean-events 18 | blueocean-git-pipeline 19 | blueocean-github-pipeline 20 | blueocean-i18n 21 | blueocean-jira 22 | blueocean-jwt 23 | blueocean-personalization 24 | blueocean-pipeline-api-impl 25 | blueocean-pipeline-editor 26 | blueocean-pipeline-scm-api 27 | blueocean-rest 28 | blueocean-rest-impl 29 | blueocean-web 30 | bouncycastle-api 31 | branch-api 32 | build-pipeline-plugin 33 | cloudbees-bitbucket-branch-source 34 | cloudbees-folder 35 | command-launcher 36 | conditional-buildstep 37 | config-file-provider 38 | credentials 39 | credentials-binding 40 | display-url-api 41 | docker-commons 42 | docker-workflow 43 | durable-task 44 | email-ext 45 | embeddable-build-status 46 | external-monitor-job 47 | favorite 48 | git 49 | git-client 50 | git-server 51 | github 52 | github-api 53 | github-branch-source 54 | github-pullrequest 55 | handlebards 56 | handy-uri-templates-2-api 57 | htmlpublisher 58 | jackson2-api 59 | javadoc 60 | jdk-tool 61 | jenkins-design-language 62 | jira 63 | jquery 64 | jquery-detached 65 | jsch 66 | junit 67 | ldap 68 | mailer 69 | matrix-auth 70 | matrix-project 71 | maven-plugin 72 | mercurial 73 | momentjs 74 | pam-auth 75 | parameterized-trigger 76 | pipeline-build-step 77 | pipeline-graph-analysis 78 | pipeline-input-step 79 | pipeline-milestone-step 80 | pipeline-model-api 81 | pipeline-model-declarative-agent 82 | pipeline-model-definition 83 | pipeline-model-extensions 84 | pipeline-multibranch-defaults 85 | pipeline-rest-api 86 | pipeline-stage-step 87 | pipeline-stage-tags-metadata 88 | pipeline-stage-view 89 | plain-credentials 90 | pubsub-light 91 | run-condition 92 | scm-api 93 | script-security 94 | slack 95 | see-gateway 96 | ssh 97 | ssh-agent 98 | ssh-credentials 99 | ssh-slaves 100 | structs 101 | token-macro 102 | variant 103 | windows-slaves 104 | workflow-aggregator 105 | workflow-api 106 | workflow-basic-steps 107 | workflow-cps 108 | workflow-cps-global-lib 109 | workflow-durable-task-step 110 | workflow-job 111 | workflow-multibranch 112 | workflow-scm-step 113 | workflow-step-api 114 | workflow-support -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/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 https://jenkins-ci.org/redhat/jenkins-ci.org.key 8 | yum install -y jenkins 9 | chkconfig jenkins on 10 | 11 | echo "Install Telegraf" 12 | wget https://dl.influxdata.com/telegraf/releases/telegraf-1.6.0-1.x86_64.rpm -O /tmp/telegraf.rpm 13 | yum localinstall -y /tmp/telegraf.rpm 14 | rm /tmp/telegraf.rpm 15 | chkconfig telegraf on 16 | mv /tmp/telegraf.conf /etc/telegraf/telegraf.conf 17 | service telegraf start 18 | 19 | echo "Install git" 20 | yum install -y git 21 | 22 | echo "Setup SSH key" 23 | mkdir /var/lib/jenkins/.ssh 24 | touch /var/lib/jenkins/.ssh/known_hosts 25 | chmod 700 /var/lib/jenkins/.ssh 26 | cp /tmp/id_rsa /var/lib/jenkins/.ssh/id_rsa && chown jenkins:jenkins /tmp/id_rsa 27 | mv /tmp/id_rsa.pub /var/lib/jenkins/.ssh/id_rsa.pub 28 | chown -R jenkins:jenkins /var/lib/jenkins/.ssh 29 | chmod 600 /var/lib/jenkins/.ssh/id_rsa /var/lib/jenkins/.ssh/id_rsa.pub 30 | 31 | echo "Configure Jenkins" 32 | mkdir -p /var/lib/jenkins/init.groovy.d 33 | mv /tmp/basic-security.groovy /var/lib/jenkins/init.groovy.d/basic-security.groovy 34 | mv /tmp/disable-cli.groovy /var/lib/jenkins/init.groovy.d/disable-cli.groovy 35 | mv /tmp/csrf-protection.groovy /var/lib/jenkins/init.groovy.d/csrf-protection.groovy 36 | mv /tmp/disable-jnlp.groovy /var/lib/jenkins/init.groovy.d/disable-jnlp.groovy 37 | mv /tmp/jenkins.install.UpgradeWizard.state /var/lib/jenkins/jenkins.install.UpgradeWizard.state 38 | mv /tmp/node-agent.groovy /var/lib/jenkins/init.groovy.d/node-agent.groovy 39 | chown -R jenkins:jenkins /var/lib/jenkins/jenkins.install.UpgradeWizard.state 40 | mv /tmp/jenkins /etc/sysconfig/jenkins 41 | chmod +x /tmp/install-plugins.sh 42 | bash /tmp/install-plugins.sh 43 | service jenkins start -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/telegraf.conf: -------------------------------------------------------------------------------- 1 | [global_tags] 2 | hostname="Jenkins" 3 | tool="Packer" 4 | 5 | # Read metrics about CPU usage 6 | [[inputs.cpu]] 7 | percpu = false 8 | totalcpu = true 9 | fieldpass = [ "usage*" ] 10 | name_suffix = "_vm" 11 | 12 | # Read metrics about disk usagee 13 | [[inputs.disk]] 14 | fielddrop = [ "inodes*" ] 15 | mount_points=["/"] 16 | name_suffix = "_vm" 17 | 18 | # Read metrics about network usage 19 | [[inputs.net]] 20 | interfaces = [ "eth0", "eth1" ] 21 | fielddrop = [ "icmp*", "ip*", "tcp*", "udp*" ] 22 | name_suffix = "_vm" 23 | 24 | # Read metrics about memory usage 25 | [[inputs.mem]] 26 | name_suffix = "_vm" 27 | 28 | # Read metrics about swap memory usage 29 | [[inputs.swap]] 30 | name_suffix = "_vm" 31 | 32 | # Read metrics about system load & uptime 33 | [[inputs.system]] 34 | name_suffix = "_vm" 35 | 36 | [[outputs.influxdb]] 37 | database = "instances" 38 | urls = ["http://10.0.0.10:8086"] 39 | namepass = ["*_vm"] -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-master/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "eu-west-2", 4 | "source_ami" : "ami-0274e11dced17bb5b" 5 | }, 6 | "builders" : [ 7 | { 8 | "type" : "amazon-ebs", 9 | "profile" : "default", 10 | "region" : "{{user `region`}}", 11 | "instance_type" : "t2.micro", 12 | "source_ami" : "{{user `source_ami`}}", 13 | "ssh_username" : "ec2-user", 14 | "ami_name" : "jenkins-master-2.150.1", 15 | "ami_description" : "Amazon Linux Image with Jenkins Server", 16 | "run_tags" : { 17 | "Name" : "packer-builder-docker" 18 | }, 19 | "tags" : { 20 | "Tool" : "Packer", 21 | "Author" : "mlabouardy" 22 | } 23 | } 24 | ], 25 | "provisioners" : [ 26 | { 27 | "type" : "file", 28 | "source" : "./basic-security.groovy", 29 | "destination" : "/tmp/basic-security.groovy" 30 | }, 31 | { 32 | "type" : "file", 33 | "source" : "./jenkins.install.UpgradeWizard.state", 34 | "destination" : "/tmp/jenkins.install.UpgradeWizard.state" 35 | }, 36 | { 37 | "type" : "file", 38 | "source" : "./disable-cli.groovy", 39 | "destination" : "/tmp/disable-cli.groovy" 40 | }, 41 | { 42 | "type" : "file", 43 | "source" : "./csrf-protection.groovy", 44 | "destination" : "/tmp/csrf-protection.groovy" 45 | }, 46 | { 47 | "type" : "file", 48 | "source" : "./disable-jnlp.groovy", 49 | "destination" : "/tmp/disable-jnlp.groovy" 50 | }, 51 | { 52 | "type" : "file", 53 | "source" : "./jenkins", 54 | "destination" : "/tmp/jenkins" 55 | }, 56 | { 57 | "type" : "file", 58 | "source" : "/Users/mlabouardy/.ssh/id_rsa", 59 | "destination" : "/tmp/id_rsa" 60 | }, 61 | { 62 | "type" : "file", 63 | "source" : "/Users/mlabouardy/.ssh/id_rsa.pub", 64 | "destination" : "/tmp/id_rsa.pub" 65 | }, 66 | { 67 | "type" : "file", 68 | "source" : "./node-agent.groovy", 69 | "destination" : "/tmp/node-agent.groovy" 70 | }, 71 | { 72 | "type" : "file", 73 | "source" : "./plugins.txt", 74 | "destination" : "/tmp/plugins.txt" 75 | }, 76 | { 77 | "type" : "file", 78 | "source" : "./install-plugins.sh", 79 | "destination" : "/tmp/install-plugins.sh" 80 | }, 81 | { 82 | "type" : "file", 83 | "source" : "./telegraf.conf", 84 | "destination" : "/tmp/telegraf.conf" 85 | }, 86 | { 87 | "type" : "shell", 88 | "script" : "./setup.sh", 89 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 90 | } 91 | ] 92 | } 93 | -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-slave/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 Telegraf" 17 | wget https://dl.influxdata.com/telegraf/releases/telegraf-1.6.0-1.x86_64.rpm -O /tmp/telegraf.rpm 18 | yum localinstall -y /tmp/telegraf.rpm 19 | rm /tmp/telegraf.rpm 20 | chkconfig telegraf on 21 | usermod -aG docker telegraf 22 | mv /tmp/telegraf.conf /etc/telegraf/telegraf.conf 23 | service telegraf start -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-slave/telegraf.conf: -------------------------------------------------------------------------------- 1 | [global_tags] 2 | hostname="Slaves" 3 | tool="Packer" 4 | 5 | # Read metrics about CPU usage 6 | [[inputs.cpu]] 7 | percpu = false 8 | totalcpu = true 9 | fieldpass = [ "usage*" ] 10 | name_suffix = "_vm" 11 | 12 | # Read metrics about disk usagee 13 | [[inputs.disk]] 14 | fielddrop = [ "inodes*" ] 15 | mount_points=["/"] 16 | name_suffix = "_vm" 17 | 18 | # Read metrics about network usage 19 | [[inputs.net]] 20 | interfaces = [ "eth0", "eth1" ] 21 | fielddrop = [ "icmp*", "ip*", "tcp*", "udp*" ] 22 | name_suffix = "_vm" 23 | 24 | # Read metrics about memory usage 25 | [[inputs.mem]] 26 | name_suffix = "_vm" 27 | 28 | # Read metrics about swap memory usage 29 | [[inputs.swap]] 30 | name_suffix = "_vm" 31 | 32 | # Read metrics about system load & uptime 33 | [[inputs.system]] 34 | name_suffix = "_vm" 35 | 36 | # Read metrics from docker socket api 37 | [[inputs.docker]] 38 | endpoint = "unix:///var/run/docker.sock" 39 | container_names = [] 40 | name_suffix = "_docker" 41 | 42 | [[outputs.influxdb]] 43 | database = "instances" 44 | urls = ["http://10.0.0.10:8086"] 45 | namepass = ["*_vm"] 46 | 47 | [[outputs.influxdb]] 48 | database = "containers" 49 | urls = ["http://10.0.0.10:8086"] 50 | namepass = ["*_docker"] -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/packer/jenkins-slave/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables" : { 3 | "region" : "eu-west-2", 4 | "source_ami" : "ami-01419b804382064e4" 5 | }, 6 | "builders" : [ 7 | { 8 | "type" : "amazon-ebs", 9 | "profile" : "default", 10 | "region" : "{{user `region`}}", 11 | "instance_type" : "t2.micro", 12 | "source_ami" : "{{user `source_ami`}}", 13 | "ssh_username" : "ec2-user", 14 | "ami_name" : "jenkins-slave", 15 | "ami_description" : "Amazon Linux Image for Jenkins Slave", 16 | "run_tags" : { 17 | "Name" : "packer-builder-docker" 18 | }, 19 | "tags" : { 20 | "Tool" : "Packer", 21 | "Author" : "mlabouardy" 22 | } 23 | } 24 | ], 25 | "provisioners" : [ 26 | { 27 | "type" : "file", 28 | "source" : "./telegraf.conf", 29 | "destination" : "/tmp/telegraf.conf" 30 | }, 31 | { 32 | "type" : "shell", 33 | "script" : "./setup.sh", 34 | "execute_command" : "sudo -E -S sh '{{ .Path }}'" 35 | } 36 | ] 37 | } -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/terraform/ami.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "jenkins-master" { 2 | most_recent = true 3 | owners = ["self"] 4 | 5 | filter { 6 | name = "name" 7 | values = ["jenkins-master-2.150.1"] 8 | } 9 | } 10 | 11 | data "aws_ami" "jenkins-slave" { 12 | most_recent = true 13 | owners = ["self"] 14 | 15 | filter { 16 | name = "name" 17 | values = ["jenkins-slave"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/terraform/elb.tf: -------------------------------------------------------------------------------- 1 | // Jenkins ELB 2 | resource "aws_elb" "jenkins_elb" { 3 | subnets = ["${var.vpc_public_subnets}"] 4 | cross_zone_load_balancing = true 5 | security_groups = ["${aws_security_group.elb_jenkins_sg.id}"] 6 | instances = ["${aws_instance.jenkins_master.id}"] 7 | 8 | listener { 9 | instance_port = 8080 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:8080" 21 | interval = 5 22 | } 23 | 24 | tags { 25 | Name = "jenkins_elb" 26 | Author = "mlabouardy" 27 | Tool = "Terraform" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /deploy-a-highly-available-jenkins-cluster-on-aws/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | resource "aws_iam_instance_profile" "profile" { 2 | name = "JenkinsSlavesAccess" 3 | role = "${aws_iam_role.role.name}" 4 | } 5 | 6 | resource "aws_iam_role" "role" { 7 | name = "JenkinsSlavesRole" 8 | path = "/" 9 | 10 | assume_role_policy = < { 19 | docClient.scan(params, (err, data) => { 20 | if (err) { 21 | res.status(500).send('Something went wrong'); 22 | throw err 23 | } else { 24 | console.log("Number of movies:", data.Items.length) 25 | res.send(data.Items) 26 | } 27 | }); 28 | }) 29 | 30 | App.listen(Config.PORT, () => { 31 | console.log('Server listening on port', Config.PORT) 32 | }) 33 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/api/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.22", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "aws-sdk": { 22 | "version": "2.406.0", 23 | "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.406.0.tgz", 24 | "integrity": "sha512-/nLaTHZ7/XmLSBDmgLfJwmdO8aX5OkAN1FwCWsWcnlDac+KTe9On1ucrF6F3+35Zqv671R1kptT2gN3GaW9eKg==", 25 | "requires": { 26 | "buffer": "4.9.1", 27 | "events": "1.1.1", 28 | "ieee754": "1.1.8", 29 | "jmespath": "0.15.0", 30 | "querystring": "0.2.0", 31 | "sax": "1.2.1", 32 | "url": "0.10.3", 33 | "uuid": "3.3.2", 34 | "xml2js": "0.4.19" 35 | } 36 | }, 37 | "base64-js": { 38 | "version": "1.3.0", 39 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", 40 | "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" 41 | }, 42 | "body-parser": { 43 | "version": "1.18.3", 44 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", 45 | "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", 46 | "requires": { 47 | "bytes": "3.0.0", 48 | "content-type": "1.0.4", 49 | "debug": "2.6.9", 50 | "depd": "1.1.2", 51 | "http-errors": "1.6.3", 52 | "iconv-lite": "0.4.23", 53 | "on-finished": "2.3.0", 54 | "qs": "6.5.2", 55 | "raw-body": "2.3.3", 56 | "type-is": "1.6.16" 57 | } 58 | }, 59 | "buffer": { 60 | "version": "4.9.1", 61 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", 62 | "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", 63 | "requires": { 64 | "base64-js": "1.3.0", 65 | "ieee754": "1.1.8", 66 | "isarray": "1.0.0" 67 | } 68 | }, 69 | "bytes": { 70 | "version": "3.0.0", 71 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 72 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 73 | }, 74 | "content-disposition": { 75 | "version": "0.5.2", 76 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 77 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 78 | }, 79 | "content-type": { 80 | "version": "1.0.4", 81 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 82 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 83 | }, 84 | "cookie": { 85 | "version": "0.3.1", 86 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 87 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 88 | }, 89 | "cookie-signature": { 90 | "version": "1.0.6", 91 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 92 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 93 | }, 94 | "cors": { 95 | "version": "2.8.5", 96 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 97 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 98 | "requires": { 99 | "object-assign": "4.1.1", 100 | "vary": "1.1.2" 101 | } 102 | }, 103 | "debug": { 104 | "version": "2.6.9", 105 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 106 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 107 | "requires": { 108 | "ms": "2.0.0" 109 | } 110 | }, 111 | "depd": { 112 | "version": "1.1.2", 113 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 114 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 115 | }, 116 | "destroy": { 117 | "version": "1.0.4", 118 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 119 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 120 | }, 121 | "ee-first": { 122 | "version": "1.1.1", 123 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 124 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 125 | }, 126 | "encodeurl": { 127 | "version": "1.0.2", 128 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 129 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 130 | }, 131 | "escape-html": { 132 | "version": "1.0.3", 133 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 134 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 135 | }, 136 | "etag": { 137 | "version": "1.8.1", 138 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 139 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 140 | }, 141 | "events": { 142 | "version": "1.1.1", 143 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", 144 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" 145 | }, 146 | "express": { 147 | "version": "4.16.4", 148 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", 149 | "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", 150 | "requires": { 151 | "accepts": "1.3.5", 152 | "array-flatten": "1.1.1", 153 | "body-parser": "1.18.3", 154 | "content-disposition": "0.5.2", 155 | "content-type": "1.0.4", 156 | "cookie": "0.3.1", 157 | "cookie-signature": "1.0.6", 158 | "debug": "2.6.9", 159 | "depd": "1.1.2", 160 | "encodeurl": "1.0.2", 161 | "escape-html": "1.0.3", 162 | "etag": "1.8.1", 163 | "finalhandler": "1.1.1", 164 | "fresh": "0.5.2", 165 | "merge-descriptors": "1.0.1", 166 | "methods": "1.1.2", 167 | "on-finished": "2.3.0", 168 | "parseurl": "1.3.2", 169 | "path-to-regexp": "0.1.7", 170 | "proxy-addr": "2.0.4", 171 | "qs": "6.5.2", 172 | "range-parser": "1.2.0", 173 | "safe-buffer": "5.1.2", 174 | "send": "0.16.2", 175 | "serve-static": "1.13.2", 176 | "setprototypeof": "1.1.0", 177 | "statuses": "1.4.0", 178 | "type-is": "1.6.16", 179 | "utils-merge": "1.0.1", 180 | "vary": "1.1.2" 181 | } 182 | }, 183 | "finalhandler": { 184 | "version": "1.1.1", 185 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 186 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 187 | "requires": { 188 | "debug": "2.6.9", 189 | "encodeurl": "1.0.2", 190 | "escape-html": "1.0.3", 191 | "on-finished": "2.3.0", 192 | "parseurl": "1.3.2", 193 | "statuses": "1.4.0", 194 | "unpipe": "1.0.0" 195 | } 196 | }, 197 | "forwarded": { 198 | "version": "0.1.2", 199 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 200 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 201 | }, 202 | "fresh": { 203 | "version": "0.5.2", 204 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 205 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 206 | }, 207 | "http-errors": { 208 | "version": "1.6.3", 209 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 210 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 211 | "requires": { 212 | "depd": "1.1.2", 213 | "inherits": "2.0.3", 214 | "setprototypeof": "1.1.0", 215 | "statuses": "1.4.0" 216 | } 217 | }, 218 | "iconv-lite": { 219 | "version": "0.4.23", 220 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", 221 | "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", 222 | "requires": { 223 | "safer-buffer": "2.1.2" 224 | } 225 | }, 226 | "ieee754": { 227 | "version": "1.1.8", 228 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", 229 | "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" 230 | }, 231 | "inherits": { 232 | "version": "2.0.3", 233 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 234 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 235 | }, 236 | "ipaddr.js": { 237 | "version": "1.8.0", 238 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", 239 | "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" 240 | }, 241 | "isarray": { 242 | "version": "1.0.0", 243 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 244 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 245 | }, 246 | "jmespath": { 247 | "version": "0.15.0", 248 | "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", 249 | "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" 250 | }, 251 | "media-typer": { 252 | "version": "0.3.0", 253 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 254 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 255 | }, 256 | "merge-descriptors": { 257 | "version": "1.0.1", 258 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 259 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 260 | }, 261 | "methods": { 262 | "version": "1.1.2", 263 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 264 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 265 | }, 266 | "mime": { 267 | "version": "1.4.1", 268 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 269 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 270 | }, 271 | "mime-db": { 272 | "version": "1.38.0", 273 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", 274 | "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" 275 | }, 276 | "mime-types": { 277 | "version": "2.1.22", 278 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", 279 | "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", 280 | "requires": { 281 | "mime-db": "1.38.0" 282 | } 283 | }, 284 | "ms": { 285 | "version": "2.0.0", 286 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 287 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 288 | }, 289 | "negotiator": { 290 | "version": "0.6.1", 291 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 292 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 293 | }, 294 | "object-assign": { 295 | "version": "4.1.1", 296 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 297 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 298 | }, 299 | "on-finished": { 300 | "version": "2.3.0", 301 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 302 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 303 | "requires": { 304 | "ee-first": "1.1.1" 305 | } 306 | }, 307 | "parseurl": { 308 | "version": "1.3.2", 309 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 310 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 311 | }, 312 | "path-to-regexp": { 313 | "version": "0.1.7", 314 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 315 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 316 | }, 317 | "proxy-addr": { 318 | "version": "2.0.4", 319 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", 320 | "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", 321 | "requires": { 322 | "forwarded": "0.1.2", 323 | "ipaddr.js": "1.8.0" 324 | } 325 | }, 326 | "punycode": { 327 | "version": "1.3.2", 328 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", 329 | "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" 330 | }, 331 | "qs": { 332 | "version": "6.5.2", 333 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 334 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 335 | }, 336 | "querystring": { 337 | "version": "0.2.0", 338 | "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", 339 | "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" 340 | }, 341 | "range-parser": { 342 | "version": "1.2.0", 343 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 344 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 345 | }, 346 | "raw-body": { 347 | "version": "2.3.3", 348 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", 349 | "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", 350 | "requires": { 351 | "bytes": "3.0.0", 352 | "http-errors": "1.6.3", 353 | "iconv-lite": "0.4.23", 354 | "unpipe": "1.0.0" 355 | } 356 | }, 357 | "safe-buffer": { 358 | "version": "5.1.2", 359 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 360 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 361 | }, 362 | "safer-buffer": { 363 | "version": "2.1.2", 364 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 365 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 366 | }, 367 | "sax": { 368 | "version": "1.2.1", 369 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", 370 | "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" 371 | }, 372 | "send": { 373 | "version": "0.16.2", 374 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 375 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 376 | "requires": { 377 | "debug": "2.6.9", 378 | "depd": "1.1.2", 379 | "destroy": "1.0.4", 380 | "encodeurl": "1.0.2", 381 | "escape-html": "1.0.3", 382 | "etag": "1.8.1", 383 | "fresh": "0.5.2", 384 | "http-errors": "1.6.3", 385 | "mime": "1.4.1", 386 | "ms": "2.0.0", 387 | "on-finished": "2.3.0", 388 | "range-parser": "1.2.0", 389 | "statuses": "1.4.0" 390 | } 391 | }, 392 | "serve-static": { 393 | "version": "1.13.2", 394 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 395 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 396 | "requires": { 397 | "encodeurl": "1.0.2", 398 | "escape-html": "1.0.3", 399 | "parseurl": "1.3.2", 400 | "send": "0.16.2" 401 | } 402 | }, 403 | "setprototypeof": { 404 | "version": "1.1.0", 405 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 406 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 407 | }, 408 | "statuses": { 409 | "version": "1.4.0", 410 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 411 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 412 | }, 413 | "type-is": { 414 | "version": "1.6.16", 415 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 416 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 417 | "requires": { 418 | "media-typer": "0.3.0", 419 | "mime-types": "2.1.22" 420 | } 421 | }, 422 | "unpipe": { 423 | "version": "1.0.0", 424 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 425 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 426 | }, 427 | "url": { 428 | "version": "0.10.3", 429 | "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", 430 | "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", 431 | "requires": { 432 | "punycode": "1.3.2", 433 | "querystring": "0.2.0" 434 | } 435 | }, 436 | "utils-merge": { 437 | "version": "1.0.1", 438 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 439 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 440 | }, 441 | "uuid": { 442 | "version": "3.3.2", 443 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 444 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 445 | }, 446 | "vary": { 447 | "version": "1.1.2", 448 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 449 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 450 | }, 451 | "xml2js": { 452 | "version": "0.4.19", 453 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", 454 | "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", 455 | "requires": { 456 | "sax": "1.2.1", 457 | "xmlbuilder": "9.0.7" 458 | } 459 | }, 460 | "xmlbuilder": { 461 | "version": "9.0.7", 462 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", 463 | "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" 464 | } 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "RESTful API based on DynamoDB", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "author": "Mohamed Labouardy", 10 | "license": "MIT", 11 | "dependencies": { 12 | "aws-sdk": "^2.406.0", 13 | "cors": "^2.8.5", 14 | "express": "^4.16.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/frontend/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const webserver = require('gulp-webserver'); 3 | const replace = require('gulp-replace'); 4 | 5 | gulp.task('replace', () => { 6 | gulp.src('index.html') 7 | .pipe(replace('API_URL', 'http://localhost:3000')) 8 | .pipe(gulp.dest('build/')) 9 | }) 10 | 11 | gulp.task('webserver', () => { 12 | gulp.src('build') 13 | .pipe(webserver({ 14 | livereload: true, 15 | directoryListing: false, 16 | open: true 17 | })); 18 | }); -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Movies Database 4 | 5 | 6 | 7 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 |
CoverTitleDescriptionUser score
26 | 27 | {{movie.title}}{{movie.description}}{{movie.userscore}} %
34 | 35 | 44 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "gulpfile.js", 6 | "dependencies": { 7 | "gulp": "^4.0.0", 8 | "gulp-replace": "^1.0.0", 9 | "gulp-webserver": "^0.9.1" 10 | }, 11 | "devDependencies": {}, 12 | "scripts": { 13 | "start": "gulp webserver" 14 | }, 15 | "author": "Mohamed Labouardy", 16 | "license": "MIT" 17 | } 18 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/application/worker/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "time" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/aws/aws-sdk-go-v2/aws/external" 15 | "github.com/aws/aws-sdk-go-v2/service/dynamodb" 16 | "github.com/aws/aws-sdk-go-v2/service/sqs" 17 | "github.com/aws/aws-sdk-go/aws" 18 | "github.com/rs/xid" 19 | ) 20 | 21 | var ( 22 | sqsClient *sqs.SQS 23 | dynamoClient *dynamodb.DynamoDB 24 | ) 25 | 26 | type Movie struct { 27 | ID string ` json:"id"` 28 | Title string `json:"title"` 29 | Cover string `json:"cover"` 30 | Description string `json:"description"` 31 | UserScore float64 `json:"userscore"` 32 | } 33 | 34 | func crawlPage(url string) (string, error) { 35 | client := &http.Client{} 36 | req, err := http.NewRequest("GET", url, nil) 37 | if err != nil { 38 | return "", err 39 | } 40 | 41 | resp, err := client.Do(req) 42 | if err != nil { 43 | return "", err 44 | } 45 | defer resp.Body.Close() 46 | 47 | data, err := ioutil.ReadAll(resp.Body) 48 | return string(data), err 49 | } 50 | 51 | func parseHTML(content string) (Movie, error) { 52 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) 53 | if err != nil { 54 | return Movie{}, err 55 | } 56 | 57 | title := doc.Find(".title span a").Text() 58 | percent, _ := doc.Find(".user_score_chart").Attr("data-percent") 59 | userScore, _ := strconv.ParseFloat(percent, 64) 60 | description := doc.Find(".overview p").Text() 61 | img, _ := doc.Find("div.poster div.image_content img").Attr("srcset") 62 | cover := strings.Split(img, " 1x")[0] 63 | 64 | return Movie{ 65 | Title: title, 66 | Cover: cover, 67 | Description: description, 68 | UserScore: userScore, 69 | }, nil 70 | } 71 | 72 | func getMessages() ([]sqs.Message, error) { 73 | req := sqsClient.ReceiveMessageRequest(&sqs.ReceiveMessageInput{ 74 | QueueUrl: aws.String(os.Getenv("QUEUE_URL")), 75 | }) 76 | resp, err := req.Send() 77 | return resp.Messages, err 78 | } 79 | 80 | func deleteMessage(receiptHandle *string) error { 81 | req := sqsClient.DeleteMessageRequest(&sqs.DeleteMessageInput{ 82 | QueueUrl: aws.String(os.Getenv("QUEUE_URL")), 83 | ReceiptHandle: receiptHandle, 84 | }) 85 | _, err := req.Send() 86 | return err 87 | } 88 | 89 | func insertMovie(movie Movie) error { 90 | req := dynamoClient.PutItemRequest(&dynamodb.PutItemInput{ 91 | TableName: aws.String(os.Getenv("TABLE_NAME")), 92 | Item: map[string]dynamodb.AttributeValue{ 93 | "id": dynamodb.AttributeValue{ 94 | S: aws.String(xid.New().String()), 95 | }, 96 | "title": dynamodb.AttributeValue{ 97 | S: aws.String(movie.Title), 98 | }, 99 | "cover": dynamodb.AttributeValue{ 100 | S: aws.String(movie.Cover), 101 | }, 102 | "description": dynamodb.AttributeValue{ 103 | S: aws.String(movie.Description), 104 | }, 105 | "userscore": dynamodb.AttributeValue{ 106 | N: aws.String(fmt.Sprintf("%v", movie.UserScore)), 107 | }, 108 | }, 109 | }) 110 | _, err := req.Send() 111 | return err 112 | } 113 | 114 | func init() { 115 | cfg, err := external.LoadDefaultAWSConfig() 116 | if err != nil { 117 | log.Fatal(err) 118 | } 119 | 120 | sqsClient = sqs.New(cfg) 121 | dynamoClient = dynamodb.New(cfg) 122 | } 123 | 124 | func main() { 125 | for { 126 | messages, err := getMessages() 127 | if err != nil { 128 | log.Fatal(err) 129 | } 130 | 131 | if len(messages) > 0 { 132 | log.Println("Crawling:", *messages[0].Body) 133 | 134 | html, err := crawlPage(*messages[0].Body) 135 | if err != nil { 136 | log.Fatal(err) 137 | } 138 | movie, err := parseHTML(html) 139 | 140 | log.Println("Movie:", movie) 141 | 142 | err = insertMovie(movie) 143 | if err != nil { 144 | log.Fatal(err) 145 | } 146 | 147 | err = deleteMessage(messages[0].ReceiptHandle) 148 | if err != nil { 149 | log.Fatal(err) 150 | } 151 | } else { 152 | log.Println("Empty queue") 153 | } 154 | 155 | time.Sleep(5 * time.Second) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/terraform/dynamodb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "table" { 2 | name = "${var.table}" 3 | billing_mode = "PROVISIONED" 4 | read_capacity = 5 5 | write_capacity = 5 6 | hash_key = "id" 7 | 8 | attribute { 9 | name = "id" 10 | type = "S" 11 | } 12 | 13 | tags = { 14 | Author = "mlabouardy" 15 | Tool = "Terraform" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "SQS URL" { 2 | value = "${aws_sqs_queue.queue.id}" 3 | } 4 | 5 | output "Table name" { 6 | value = "${aws_dynamodb_table.table.name}" 7 | } 8 | 9 | output "Website URL" { 10 | value = "${aws_s3_bucket.bucket.website_endpoint}" 11 | } 12 | -------------------------------------------------------------------------------- /implement-a-cicd-pipeline-for-dockerized-microservices/terraform/s3.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "bucket" { 2 | bucket = "${var.bucket}" 3 | acl = "public-read" 4 | 5 | website { 6 | index_document = "index.html" 7 | error_document = "error.html" 8 | } 9 | 10 | tags { 11 | Author = "mlabouardy" 12 | Tool = "Terraform" 13 | } 14 | } 15 | 16 | resource "aws_s3_bucket_policy" "policy" { 17 | bucket = "${aws_s3_bucket.bucket.id}" 18 | 19 | policy = <