├── .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 | Cover |
18 | Title |
19 | Description |
20 | User score |
21 |
22 |
23 |
24 |
25 |
26 |
27 | |
28 | {{movie.title}} |
29 | {{movie.description}} |
30 | {{movie.userscore}} % |
31 |
32 |
33 |
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 = <