├── Chapter03 ├── README.html ├── crm1 │ ├── Procfile │ ├── README.md │ ├── profiles.clj │ ├── project.clj │ ├── resources │ │ ├── docs │ │ │ └── docs.md │ │ ├── public │ │ │ └── css │ │ │ │ └── screen.css │ │ └── templates │ │ │ ├── about.html │ │ │ ├── base.html │ │ │ ├── error.html │ │ │ ├── home.html │ │ │ ├── searchuser.html │ │ │ └── useradd.html │ ├── src │ │ └── usermanager │ │ │ ├── core.clj │ │ │ ├── handler.clj │ │ │ ├── layout.clj │ │ │ ├── middleware.clj │ │ │ └── routes │ │ │ └── home.clj │ └── test │ │ └── usermanager │ │ └── test │ │ └── handler.clj └── liquibase-helloworld │ ├── README.org │ ├── pom.xml │ └── src │ └── main │ └── resources │ └── db-changelog.xml ├── Chapter06 ├── autotest_v1 │ ├── bin │ │ └── autotest.sh │ ├── pom.xml │ └── src │ │ └── test │ │ ├── java │ │ └── practdev │ │ │ └── UserManagerSeleniumTest.java │ │ └── resources │ │ └── features │ │ └── manageusers.feature ├── hello-cucumber6 │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── practdev │ │ │ └── Calculator.java │ │ └── test │ │ ├── java │ │ └── practdev │ │ │ ├── CalculatorSteps.java │ │ │ └── CalculatorTest.java │ │ └── resources │ │ └── practdev │ │ └── addition.feature ├── hello-cucumber8 │ ├── README.md │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── practdev │ │ │ └── Calculator.java │ │ └── test │ │ ├── java │ │ └── practdev │ │ │ ├── CalculatorSteps.java │ │ │ └── CalculatorTest.java │ │ └── resources │ │ └── practdev │ │ └── addition.feature ├── hello-junit │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── practdev │ │ │ └── App.java │ │ └── test │ │ └── java │ │ └── practdev │ │ └── AppTest.java ├── hello-selenium │ ├── pom.xml │ └── src │ │ └── test │ │ └── java │ │ └── practdev │ │ └── HelloSeleniumTest.java └── usermanager │ ├── profiles.clj │ ├── project.clj │ ├── resources │ ├── docs │ │ └── docs.md │ ├── public │ │ └── css │ │ │ └── screen.css │ └── templates │ │ ├── about.html │ │ ├── base.html │ │ ├── error.html │ │ ├── home.html │ │ ├── searchresult.html │ │ ├── searchuser.html │ │ ├── useradd.html │ │ ├── useradded.html │ │ ├── userdelete.html │ │ └── userdeleted.html │ ├── src │ └── usermanager │ │ ├── core.clj │ │ ├── handler.clj │ │ ├── layout.clj │ │ ├── middleware.clj │ │ └── routes │ │ └── home.clj │ └── test │ └── usermanager │ └── test │ └── handler.clj ├── Chapter07 ├── ansdocker │ ├── Dockerfile │ └── ansible │ │ ├── inventory │ │ └── playbook.yml └── salt │ └── srv │ └── salt │ ├── state │ ├── top.sls │ └── webserver.sls │ ├── top.sls │ └── webserver.sls ├── Chapter08 └── ganglia │ └── docker-compose.yml ├── LICENSE └── README.md /Chapter03/README.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 97 | 143 | 144 | 145 |
146 |
147 |

Table of Contents

148 | 217 |
218 |
219 |

1 Code samples for the Practical Devops book

220 |
221 |

222 | These are the code samples for the book "Practical Devops" by Joakim 223 | Verona published by Packt Publishing. 224 |

225 | 226 |

227 | The books home page: 228 | https://www.packtpub.com/networking-and-servers/practical-devops 229 |

230 | 231 |

232 | In most cases you will need the book to make the most out of the exercises. 233 |

234 | 235 |

236 | Please ensure that you have the latest version of these exercises 237 | before continuing. 238 |

239 | 240 |

241 | To get the code samples from Github do: 242 |

243 |
244 | 245 |
git clone https://github.com/jave/practicaldevops.git
 246 | 
247 |
248 | 249 |

250 | And to keep the samples updated, 251 |

252 |
253 | 254 |
cd practicaldevops
 255 | git pull https://github.com/jave/practicaldevops.git
 256 | 
257 |
258 |
259 | 260 |
261 |

1.1 ch3 How Architecture affects DevOps

262 |
263 |
264 |

1.1.1 Liquibase hello-world

265 |
266 |
267 | 268 |
cd ch3/liquibase-helloworld
 269 | mvn liquibase:update
 270 | 
271 |
272 |
273 |
274 | 275 |
276 |

1.1.2 Manual installation

277 |
278 |
279 | 280 |
dnf install postgresql
 281 | dnf install nginx
 282 | cd ch3/crm1
 283 | lein build
 284 | lein run
 285 | 
286 |
287 |
288 |
289 |
290 |
291 |

1.2 ch4 Everything is code

292 |
293 |
294 |

1.2.1 Docker intermission

295 |
296 |

297 | These instructions are for Fedora, but they are similar for other 298 | distributions such as Ubuntu. 299 |

300 | 301 |

302 | To make sure Docker is working properly, 303 | see the following documentation for Fedora. 304 |

305 | 306 |

307 | https://docs.docker.com/v1.5/installation/fedora/ 308 |

309 | 310 |
    311 |
  • For fedora 21 and later do:
  • 312 |
313 |
314 | 315 |
dnf  -y install docker
 316 | 
317 |
318 | 319 |
    320 |
  • docker-io was renamed to docker from Fedora 21, so use "docker-io" on older red hat 321 | derivates, "docker" on newer
  • 322 | 323 |
  • Use a sudo capable user to run docker commands, or the root user
  • 324 | 325 |
  • You can also add a docker group with rights to use the docker socket 326 | needed to communicate with the docker daemon.
  • 327 |
328 | 329 |

330 | This approach is described here 331 | https://docs.docker.com/v1.5/installation/fedora/ 332 |

333 | 334 |

335 | In summary: 336 |

337 |
338 | 339 |
$ sudo groupadd docker
 340 | $ sudo chown root:docker /var/run/docker.sock
 341 | $ sudo usermod -a -G docker $USERNAME
 342 | 
343 |
344 | 345 |
    346 |
  • You might need "setenforce 0" to start docker. The comand will 347 | disable selinux, which has security implications. Use this only on a 348 | test machine.
  • 349 | 350 |
  • To start and enable docker on reboot:
  • 351 |
352 |
353 | 354 |
sudo systemctl start docker
 355 | sudo systemctl enable docker
 356 | 
357 |
358 | 359 |

360 | To verify that docker works: 361 |

362 |
363 | 364 |
sudo docker run -i -t fedora /bin/bash
 365 | 
366 |
367 | 368 |

369 | For some exercises you need to have docker-compose installed first. 370 |

371 | 372 |

373 | On Fedora 23 you can do: 374 |

375 |
376 | 377 |
dnf install docker-compose
 378 | 
379 |
380 | 381 |

382 | In earlier versions you needed to download docker-compose manually. 383 |

384 |
385 |
386 |
387 |

1.2.2 setting up a basic git server

388 |
389 |

390 | bare repo: 391 |

392 |
393 | 394 |
cd /opt/git 
 395 | mkdir project.git
 396 | cd project.git
 397 | git init --bare
 398 | 
399 |
400 | 401 |
    402 |
  • Now try cloning, making changes, and pushing to the server
  • 403 |
404 |
405 |
406 |
407 |

1.2.3 Gerrit

408 |
409 |

410 | Run a Gerrit container: 411 |

412 |
413 | 414 |
docker run -d -p 8080:8080 -p 29418:29418 openfrontier/gerrit
 415 | 
416 |
417 | 418 |

419 | On the host machine you can now install the supporting git-review 420 | package: 421 |

422 |
423 | 424 |
sudo dnf install git-review
 425 | 
426 |
427 | 428 |

429 | Rebase your commits on top of the commits in the remote repository: 430 |

431 |
432 | 433 |
git pull --rebase origin master
 434 | 
435 |
436 | 437 |

438 | Interactively edit the history, possibly squashing commits together to 439 | make a more readable history: 440 |

441 |
442 | 443 |
git rebase -i origin/master
 444 | 
445 |
446 |
447 |
448 | 449 | 450 |
451 |

1.2.4 Gitlab

452 |
453 |

454 | Now create a directory for gitlab, and fetch the compose file: 455 |

456 |
457 | 458 |
mkdir gitlab 
 459 | cd gitlab 
 460 | wget https://raw.githubusercontent.com/sameersbn/docker-gitlab/master/docker-compose.yml
 461 | 
462 |
463 | 464 |

465 | Now start the gitlab stack. 466 |

467 |
468 | 469 |
docker-compose up
 470 | 
471 |
472 | 473 |

474 | When the containers are up and running, access the web ui: 475 |

476 | 477 |

478 | http://loaclhost:10080 479 |

480 | 481 |

482 | and enter the following credentials: 483 |

484 | 485 |
    486 |
  • username: root
  • 487 |
  • password: 5iveL!fe
  • 488 |
489 |
490 |
491 |
492 | 493 | 494 | 495 |
496 |

1.3 ch5 Build the code

497 |
498 |

499 | Create a "freestyle" class job in Jenkins that runs the "fortune" 500 | command. 501 |

502 | 503 |

504 | First install Jenkins. 505 |

506 |
507 | 508 |
dnf install jenkins
 509 | 
510 |
511 | 512 |

513 | Then follow the instruction in the book to configure the job. 514 |

515 |
516 |
517 |

1.3.1 Cheating with FPM

518 |
519 |

520 | To install fpm: 521 |

522 |
523 | 524 |
yum install rubygems
 525 | yum install ruby
 526 | yum install ruby-devel
 527 | gem install fpm
 528 | 
529 |
530 | 531 |

532 | Package this shell script: 533 |

534 |
535 | 536 |
#!/bin/sh
 537 | echo 'Hello World!'
 538 | 
 539 | chmod a+x usr/local/bin/hello.sh
 540 | fpm -s dir -t rpm -n hello-world -v 1 -C installdir usr
 541 | 
 542 | rpm -qivp hello-world.rpm
 543 | rpm -ivh hello-world.rpm
 544 | 
545 |
546 |
547 |
548 |
549 |

1.3.2 Build servers, Jenkins in particular

550 |
551 |
552 | 553 |
dnf install jenkins
 554 | 
555 |
556 | 557 |
558 | 559 |
systemctl start jenkins
 560 | 
561 |
562 |
563 |
564 |
565 | 566 |
567 |

1.4 ch6 Test the code

568 |
569 |
570 |

1.4.1 A Junit example

571 |
572 |
573 | 574 |
cd ch6/hello-junit
 575 | mvn install
 576 | 
577 |
578 |
579 |
580 |
581 |

1.4.2 Arquilian

582 |
583 |

584 | There is an arquillian hello-world in the Arquillian documentation. 585 |

586 |
587 | 588 |
git clone https://github.com/aslakknutsen/arquillian-example-helloworld.git
 589 | cd arquillian-example-helloworld
 590 | mvn install
 591 | 
592 |
593 |
594 |
595 | 596 |
597 |

1.4.3 Automated acceptance testing

598 |
599 |

600 | There are two implementations, one with annotations, and one with 601 | Lambda notation. 602 |

603 | 604 |

605 | While the lambda notation is easier to read than the annotation 606 | syntax, cucumbers lambda notation is fairly new and can be problematic 607 | to get to work depending on your Java implementation. 608 |

609 | 610 |

611 | To run the annotation based example: 612 |

613 |
614 | 615 |
cd ch6/hello-cucumber6
 616 | mvn clean test
 617 | 
618 |
619 | 620 |

621 | To run the lambda based example: 622 |

623 |
624 | 625 |
cd ch6/hello-cucumber8
 626 | mvn clean test
 627 | 
628 |
629 |
630 |
631 | 632 | 633 | 634 |
635 |

1.4.4 A complete test automation scenario

636 |
637 |
  1. hello-selenium-world
    638 |

    639 | Hello selenium world is a minimal selenium example that should 640 | open a firefox browser window and ask google 'hello world'. 641 | You should see a list of search matches for 'hello world'. 642 |

    643 | 644 |

    645 | It is useful to check that this example runs before testing other examples. 646 | To run it: 647 |

    648 |
    649 | 650 |
    cd ch6/hello-selenium
     651 | mvn test
     652 | 
    653 |
    654 |
  2. 655 |
  3. Running the usermanager example manually
    656 |

    657 | You will need Leiningen, http://leiningen.org/ 658 | https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein 659 |

    660 |
    661 | 662 |
    ch6/usermanager
     663 | lein run
     664 | 
    665 |
    666 |
  4. 667 |
  5. Running the automated test
    668 |
    669 | 670 |
    autotest_v1/bin/autotest.sh
     671 | 
    672 |
    673 |
  6. 674 |
  7. Handling the tricky dependencies with Docker
    675 |
    676 | 677 |
    docker run -d -p 4444:4444 --name selenium-hub selenium/hub
     678 | docker run -d --link selenium-hub:hub selenium/node-firefox
     679 | 
    680 |
    681 |
682 |
683 |
684 |
685 |

1.5 ch7 Deploying the code

686 |
687 |
688 |

1.5.1 Executing code on the client

689 |
690 |
691 | 692 |
salt -E '.*' cmd.run 'ls -l'
 693 | 
694 |
695 |
696 |
697 | 698 |
699 |

1.5.2 Puppet master, Puppet agent

700 |
701 |

702 | rfkrocktk/puppet is a convenient docker image for exploring puppet. 703 |

704 | 705 | 709 | 710 |
711 | 712 |
docker --name dockerduck --hostname dockerduck -e PUPPETMASTER_TCP_HOST=ultramaster.example.com \
 713 |     -v /var/lib/docker/dockercontainer/puppet/ssl:/var/lib/puppet/ssl rfkrocktk/puppet
 714 | 
715 |
716 |
717 |
718 | 719 |
720 |

1.5.3 Ansible

721 |
722 |
723 | 724 |
FROM williamyeh/ansible:centos7
 725 | 
726 |
727 | 728 |
729 | 730 |
docker run -v `pwd`/ansible:/ansible  -it <hash> bash
 731 | cd /ansible
 732 | ansible-playbook -i inventory playbook.yml    --connection=local --sudo
 733 | 
734 |
735 | 736 |

737 | A docker container which supports systemd: 738 |

739 |
740 | 741 |
FROM fedora
 742 | RUN yum -y update; yum clean all
 743 | RUN yum install  ansible sudo
 744 | RUN systemctl mask systemd-remount-fs.service dev-hugepages.mount \
 745 | sys-fs-fuse-connections.mount \
 746 | systemd-logind.service getty.target console-getty.service
 747 | RUN cp /usr/lib/systemd/system/dbus.service /etc/systemd/system/;\
 748 | sed -i 's/OOMScoreAdjust=-900//' /etc/systemd/system/dbus.service
 749 | 
 750 | VOLUME ["/sys/fs/cgroup", "/run", "/tmp"]
 751 | ENV container=docker
 752 | 
 753 | CMD ["/usr/sbin/init"]
 754 | 
755 |
756 | 757 |

758 | To run the new container: 759 |

760 |
761 | 762 |
docker run -it --rm -v /sys/fs/cgroup:/sys/fs/cgroup:ro  -v `pwd`/ansible:/ansible <hash>
 763 | 
764 |
765 | 766 |

767 | Connect to the container: 768 |

769 |
770 | 771 |
docker exec -it <hash> bash
 772 | 
773 |
774 | 775 |

776 | A slightly more advanced exercise: 777 |

778 |
779 | 780 |
---
 781 | - hosts: localhost
 782 |   vars:
 783 |     http_port: 80
 784 |     max_clients: 200
 785 |   remote_user: root
 786 |   tasks:
 787 |   - name: ensure apache is at the latest version
 788 |     yum: name=httpd state=latest
 789 |   - name: write the apache config file
 790 |     template: src=/srv/httpd.j2 dest=/etc/httpd.conf
 791 |     notify:
 792 |     - restart apache
 793 |   - name: ensure apache is running (and enable it at boot)
 794 |     service: name=httpd state=started enabled=yes
 795 |   handlers:
 796 |     - name: restart apache
 797 |       service: name=httpd state=restarted
 798 | 
799 |
800 |
801 |
802 | 803 | 804 |
805 |

1.5.4 Deploying with Chef

806 |
807 |

808 | Start a clean container for the exercise: 809 |

810 |
811 | 812 |
docker run -it ubuntu bash
 813 | 
814 |
815 | 816 |

817 | Install Chef in the container: 818 |

819 |
820 | 821 |
curl -L https://www.opscode.com/chef/install.sh | bash
 822 | 
823 |
824 | 825 |

826 | Verify the chef-solo was installed: 827 |

828 |
829 | 830 |
chef-solo -v
 831 | 
832 |
833 | 834 |

835 | Fetch and unpack a pre-rolled chef configuration: 836 |

837 |
838 | 839 |
curl -L  http://github.com/opscode/chef-repo/tarball/master -o master.tgz
 840 | tar -zxf master.tgz
 841 | mv chef-repo* chef-repo
 842 | rm master.tgz
 843 | 
844 |
845 | 846 |

847 | Create a configuration file for chef: 848 |

849 |
850 | 851 |
mkdir .chef
 852 | echo "cookbook_path [ '/root/chef-repo/cookbooks' ]" > .chef/knife.rb
 853 | 
854 |
855 | 856 |

857 | Now create a template: 858 |

859 |
860 | 861 |
knife cookbook create phpapp
 862 | 
863 |
864 |
865 |
866 | 867 |
868 |

1.5.5 Deploying with Saltstack

869 |
870 |

871 | Start a Saltstack container: 872 |

873 |
874 | 875 |
docker run -i -t --name=saltdocker_master_1 -h master -p 4505 -p 4506 \
 876 |    -p 8080 -p 8081 -e SALT_NAME=master -e SALT_USE=master \
 877 |    -v `pwd`/srv/salt:/srv/salt:rw jacksoncage/salt
 878 | 
879 |
880 | 881 |

882 | Start a shell inside the Saltstack container: 883 |

884 |
885 | 886 |
docker exec -i -t saltdocker_master_1 bash
 887 | 
888 |
889 | 890 |

891 | Salt state to install httpd: 892 |

893 |
894 | 895 |
top.sls:
 896 | base:
 897 |   '*':
 898 |     - webserver
 899 | 
 900 | webserver.sls:
 901 | apache2:               # ID declaration
 902 |   pkg:                # state declaration
 903 |     - installed       # function declaration
 904 | 
905 |
906 | 907 |

908 | Run this command to ensure the desired state: 909 |

910 |
911 | 912 |
salt-call --local state.highstate -l debug
 913 | 
914 |
915 |
916 |
917 | 918 | 919 |
920 |

1.5.6 Vagrant

921 |
922 |
923 | 924 |
yum install 'vagrant*'
 925 | 
926 |
927 | 928 |

929 | To use Vagrants Virtualbox driver, you need to set up Virtualbox 930 | according to your distribution. 931 |

932 | 933 |

934 | Create a virtual machine with Vagrant from a recipy: 935 |

936 |
937 | 938 |
vagrant init hashicorp/precise32
 939 | 
940 |
941 | 942 |

943 | Try starting the machine: 944 |

945 |
946 | 947 |
vagrant up
 948 | 
949 |
950 | 951 |

952 | You can now ssh to the machine: 953 |

954 |
955 | 956 |
vagrant ssh
 957 | 
958 |
959 | 960 |

961 | Add this to the Vagrant file: 962 |

963 |
964 | 965 |
Vagrant.configure("2") do |config|
 966 |   config.vm.box = "hashicorp/precise32"
 967 |   config.vm.provision :shell, path: "bootstrap.sh"
 968 | end
 969 | 
970 |
971 | 972 |

973 | And create a bootstrap.sh file that will install Apache httpd: 974 |

975 |
976 | 977 |
#!/usr/bin/env bash
 978 | apt-get update
 979 | apt-get install -y apache2
 980 | 
981 |
982 |
983 |
984 |
985 |
986 |

1.6 ch8 Monitoring the code

987 |
988 |
989 |

1.6.1 Nagios

990 |
991 |

992 | Start a Nagios container: 993 |

994 |
995 | 996 |
docker run -e     NAGIOSADMIN_USER=nagiosadmin -e NAGIOSAMDIN_PASS=nagios  -p 80:30000 cpuguy83/nagios
 997 | 
998 |
999 | 1000 |

1001 | Start a second container to monitor: 1002 |

1003 |
1004 | 1005 |
docker run -p 30001:80 nginx
1006 | 
1007 |
1008 | 1009 |

1010 | A docker compose file for the scenario: 1011 |

1012 |
1013 | 1014 |
nagios:
1015 |   image: mt-nagios 
1016 |   build:
1017 |     - mt-nagios
1018 |   ports:
1019 |    -  80:30000 
1020 |   environment:
1021 |     - NAGIOSADMIN_USER=nagiosadmin
1022 |     - NAGIOSAMDIN_PASS=nagios
1023 |   volumes:
1024 |    ./nagios:/etc/nagios   
1025 | nginx:
1026 |   image: nginx
1027 | 
1028 |
1029 | 1030 |

1031 | Configuration files for the Nagios example: 1032 |

1033 |
1034 | 1035 |
define host {
1036 |     name        regular-host
1037 |     use         linux-server
1038 |     register       0
1039 |     max_check_attempts   5
1040 | }
1041 | 
1042 | define host{
1043 |     use             regular-host
1044 |     host_name       client1
1045 |     address         192.168.200.15
1046 |     contact_groups  admins
1047 |     notes           test client1
1048 | }
1049 | 
1050 |
1051 |

1052 | hostgroups.cfg 1053 |

1054 | 1055 |
1056 | 1057 |
define hostgroup {
1058 |     hostgroup_name  test-group
1059 |     alias           Test Servers
1060 |     members         client1
1061 | }
1062 | 
1063 | services.cfg
1064 | #+BEGIN_SRC sh
1065 | define service {
1066 |     use                     generic-service
1067 |     hostgroup_name          test-group
1068 |     service_description     PING
1069 |     check_command           check_ping!200.0,20%!600.0,60%
1070 | }
1071 | 
1072 |
1073 | 1074 |

1075 | An example mail configuration: 1076 |

1077 |
1078 | 1079 |
define contact{
1080 |     contact_name                    matangle-admin
1081 |     use                             generic-contact
1082 |     alias                           Nagios Admin
1083 |     email                           pd-admin@matangle.com
1084 | }
1085 | 
1086 | define contactgroup{
1087 |     contactgroup_name       admins
1088 |     alias                   Nagios Administrators
1089 |     members                 matange-admin
1090 | }
1091 | 
1092 |
1093 |
1094 |
1095 | 1096 |
1097 |

1.6.2 Munin

1098 |
1099 |
1100 | 1101 |
docker run -p 30005:80 lrivallain/munin:latest
1102 | 
1103 |
1104 | 1105 |

1106 | Running commands in the munin container: 1107 |

1108 |
1109 | 1110 |
docker exec -it <hash> bash
1111 | su - munin --shell=/bin/bash
1112 | /usr/share/munin/munin-update
1113 | 
1114 |
1115 | 1116 |

1117 | If you are having trouble running munin-update, try: 1118 |

1119 |
1120 | 1121 |
chown munin.munin /var/log/munin/munin-update.log
1122 | 
1123 |
1124 | 1125 |

1126 | It may still take some time for the graphs to display. 1127 |

1128 | 1129 |

1130 | This is the code for the munin plugin: 1131 |

1132 |
1133 | 1134 |
graph_title Load average
1135 | graph_vlabel load
1136 | load.label load
1137 | 
1138 |
1139 | 1140 |

1141 | To emit data you simply print it to stdout. 1142 |

1143 | 1144 | 1145 |
1146 | 1147 |
printf "load.value "
1148 | cut -d' ' -f2  /proc/loadavg
1149 | 
1150 |
1151 | 1152 |

1153 | Here is an example script. 1154 |

1155 | 1156 | 1157 |
1158 | 1159 |
#!/bin/sh
1160 | 
1161 | case $1 in
1162 |    config)
1163 |         cat <<'EOM'
1164 | graph_title Load average
1165 | graph_vlabel load
1166 | load.label load
1167 | EOM
1168 |         exit 0;;
1169 | esac
1170 | 
1171 | printf "load.value "
1172 | cut -d' ' -f2  /proc/loadavg
1173 | 
1174 |
1175 |
1176 |
1177 | 1178 |
1179 |

1.6.3 Ganglia

1180 |
1181 |

1182 | To get help with the container: 1183 |

1184 |
1185 | 1186 |
docker run wookietreiber/ganglia --help
1187 | 
1188 |
1189 | 1190 |

1191 | To run the Ganglia container: 1192 |

1193 |
1194 | 1195 |
docker run -p 30010:80 wookietreiber/ganglia
1196 | 
1197 |
1198 |
1199 |
1200 | 1201 | 1202 |
1203 |

1.6.4 Graphite

1204 |
1205 |

1206 | Start Graphite: 1207 |

1208 |
1209 | 1210 |
docker run -it   -p 30020:80   -p 2003:2003   sitespeedio/graphite
1211 | 
1212 |
1213 | 1214 |

1215 | Try the following url: http://localhost:30020/ 1216 |

1217 |
1218 |
1219 |
1220 |

1.6.5 Log handling

1221 |
1222 |

1223 | Start Kibana and Elasticsearch: 1224 |

1225 |
1226 | 1227 |
docker run -d elasticsearch &&
1228 | docker run --link some-elasticsearch:elasticsearch -d kibana
1229 | 
1230 |
1231 |
1232 |
1233 |
1234 | 1235 |
1236 |

1.7 ch9 Issue Tracking

1237 |
1238 |
1239 |

1.7.1 Bugzilla

1240 |
1241 |
1242 | 1243 |
docker run -p 6050:80 dklawren/docker-bugzilla
1244 | 
1245 |
1246 |
1247 |
1248 |
1249 |

1.7.2 Trac

1250 |
1251 |
1252 | 1253 |
docker run -d -p 6051:8080 barogi/trac:1.0.2
1254 | 
1255 |
1256 |
1257 |
1258 |
1259 |

1.7.3 Redmine

1260 |
1261 |
1262 | 1263 |
docker run -d -p  6052:3000 redmine
1264 | 
1265 |
1266 |
1267 |
1268 |
1269 |

1.7.4 The Gitlab issue tracker

1270 |
1271 |

1272 | Trying the Gitlab CLI: 1273 |

1274 |
1275 | 1276 |
GITLAB_API_PRIVATE_TOKEN=<token from your project>
1277 | GITLAB_API_ENDPOINT=http://gitlab.matangle.com:50003/api/v3
1278 |   gitlab help Issues
1279 | 
1280 |
1281 |
1282 |
1283 |
1284 |

1.7.5 Jira

1285 |
1286 |
1287 | 1288 |
docker run -p 6053:8080 cptactionhank/atlassian-jira:latest
1289 | 
1290 |
1291 |
1292 |
1293 |
1294 | 1295 |
1296 |

1.8 ch10 The Internet of Things and DevOps

1297 |
1298 |
1299 |

1.8.1 NodeMCU

1300 |
1301 |

1302 | To get a newer firmware(please change the version to the latest 1303 | available first): 1304 |

1305 |
1306 | 1307 |
wget https://github.com/nodemcu/nodemcu-firmware/releases/download/0.9.6-dev_20150704/nodemcu_integer_0.9.6-dev_20150704.bin
1308 | 
1309 |
1310 | 1311 |

1312 | Get esptool: 1313 |

1314 |
1315 | 1316 |
git clone https://github.com/themadinventor/esptool.git
1317 | 
1318 |
1319 | 1320 |

1321 | Install pyserial: 1322 |

1323 |
1324 | 1325 |
sudo dnf install pyserial
1326 | 
1327 |
1328 | 1329 |

1330 | Burn the firmware: 1331 |

1332 |
1333 | 1334 |
sudo python ./esptool.py --port /dev/ttyUSB0 write_flash 0x00000 nodemcu_integer_0.9.6-dev_20150704.bin
1335 | 
1336 |
1337 | 1338 |

1339 | You might need additional arguments: 1340 |

1341 |
1342 | 1343 |
sudo esptool.py --port=/dev/ttyUSB0 write_flash 0x0 nodemcu_integer_0.9.6-dev_20150704.bin  -fs 32m -fm dio -ff 40m
1344 | 
1345 |
1346 | 1347 |

1348 | Do some tests to see that the connection is working: 1349 |

1350 |
1351 | 1352 |
sudo ./esptool.py read_mac
1353 | Connecting...
1354 | MAC: 18:fe:34:00:d7:21
1355 | 
1356 | sudo ./esptool.py flash_id
1357 | Connecting...
1358 | Manufacturer: e0
1359 | Device: 4016
1360 | 
1361 |
1362 | 1363 |

1364 | Try the LED: 1365 |

1366 |
1367 | 1368 |
gpio.write(0, gpio.LOW)  -- turn led on
1369 | 
1370 |
1371 | 1372 |
1373 | 1374 |
gpio.write(0, gpio.HIGH) -- turn led off
1375 | 
1376 |
1377 | 1378 |

1379 | Blink the LED in a loop: 1380 |

1381 |
1382 | 1383 |
while 1 do                     -- loop forever
1384 |       gpio.write(0, gpio.HIGH) -- turn led off
1385 |       tmr.delay(1000000)       -- wait one second
1386 |       gpio.write(0, gpio.LOW)  -- turn led on
1387 |       tmr.delay(1000000)       -- wait one second
1388 | end
1389 | 
1390 |
1391 |

1392 | To connect to a wireless network. 1393 |

1394 | 1395 |
1396 | 1397 |
wifi.setmode(wifi.STATION)
1398 | wifi.sta.config("SSID","password")
1399 | 
1400 |
1401 | 1402 |

1403 | To see the IP we got: 1404 |

1405 |
1406 | 1407 |
print(wifi.sta.getip())
1408 | 
1409 |
1410 | 1411 |

1412 | Connecting to a web server: 1413 |

1414 |
1415 | 1416 |
conn=net.createConnection(net.TCP, false) 
1417 | conn:on("receive", function(conn, pl) print(pl) end)
1418 | conn:connect(80,"121.41.33.127")
1419 | conn:send("GET / HTTP/1.1\r\nHost: www.nodemcu.com\r\n"
1420 |     .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")
1421 | 
1422 |
1423 | 1424 |

1425 | Timer: 1426 |

1427 |
1428 | 1429 |
tmr.alarm(1, 1000, 1, function() 
1430 |     print("hello world") 
1431 | end )
1432 | 
1433 |
1434 | 1435 |

1436 | Stop the timer: 1437 |

1438 |
1439 | 1440 |
tmr.stop(1)
1441 | 
1442 |
1443 |
1444 |
1445 |
1446 |
1447 |
1448 |
1449 |

Author: Joakim Verona

1450 |

Created: 2016-02-07 Sun 21:51

1451 |

Validate

1452 |
1453 | 1454 | 1455 | -------------------------------------------------------------------------------- /Chapter03/crm1/Procfile: -------------------------------------------------------------------------------- 1 | web: java $JVM_OPTS -cp target/usermanager.jar clojure.main -m usermanager.core 2 | -------------------------------------------------------------------------------- /Chapter03/crm1/README.md: -------------------------------------------------------------------------------- 1 | # usermanager 2 | 3 | 4 | 5 | ## Prerequisites 6 | 7 | You will need [Leiningen][1] 2.0 or above installed. 8 | 9 | [1]: https://github.com/technomancy/leiningen 10 | 11 | ## Running 12 | 13 | To start a web server for the application, run: 14 | 15 | lein run 16 | 17 | -------------------------------------------------------------------------------- /Chapter03/crm1/profiles.clj: -------------------------------------------------------------------------------- 1 | {:profiles/dev {:env {}} 2 | :profiles/test {:env {}}} 3 | -------------------------------------------------------------------------------- /Chapter03/crm1/project.clj: -------------------------------------------------------------------------------- 1 | (defproject usermanager "0.1.0-SNAPSHOT" 2 | 3 | :description "FIXME: write description" 4 | :url "http://example.com/FIXME" 5 | 6 | :dependencies [[org.clojure/clojure "1.7.0"] 7 | [selmer "0.9.2"] 8 | [com.taoensso/timbre "4.1.2"] 9 | [com.taoensso/tower "3.0.2"] 10 | [markdown-clj "0.9.74"] 11 | [environ "1.0.1"] 12 | [compojure "1.4.0"] 13 | [ring-webjars "0.1.1"] 14 | [ring/ring-defaults "0.1.5"] 15 | [ring "1.4.0" 16 | :exclusions [ring/ring-jetty-adapter]] 17 | [metosin/ring-middleware-format "0.6.0"] 18 | [metosin/ring-http-response "0.6.5"] 19 | [bouncer "0.3.3"] 20 | [prone "0.8.2"] 21 | [org.clojure/tools.nrepl "0.2.11"] 22 | [org.webjars/bootstrap "3.3.5"] 23 | [org.webjars/jquery "2.1.4"] 24 | [org.immutant/web "2.1.0"]] 25 | 26 | :min-lein-version "2.0.0" 27 | :uberjar-name "usermanager.jar" 28 | :jvm-opts ["-server"] 29 | 30 | :main usermanager.core 31 | 32 | :plugins [[lein-environ "1.0.1"]] 33 | :profiles 34 | {:uberjar {:omit-source true 35 | :env {:production true} 36 | :aot :all} 37 | :dev [:project/dev :profiles/dev] 38 | :test [:project/test :profiles/test] 39 | :project/dev {:dependencies [[ring/ring-mock "0.3.0"] 40 | [ring/ring-devel "1.4.0"] 41 | [pjstadig/humane-test-output "0.7.0"]] 42 | 43 | 44 | :repl-options {:init-ns usermanager.core} 45 | :injections [(require 'pjstadig.humane-test-output) 46 | (pjstadig.humane-test-output/activate!)] 47 | ;;when :nrepl-port is set the application starts the nREPL server on load 48 | :env {:dev true 49 | :port 3000 50 | :nrepl-port 7000}} 51 | :project/test {:env {:test true 52 | :port 3001 53 | :nrepl-port 7001}} 54 | :profiles/dev {} 55 | :profiles/test {}}) 56 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/docs/docs.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Managing Your Middleware 4 | 5 | Request middleware functions are located under the `usermanager.middleware` namespace. 6 | 7 | This namespace is reserved for any custom middleware for the application. Some default middleware is 8 | already defined here. 9 | 10 | The two function for organizing the middleware are called `wrap-dev` and `wrap-base`. 11 | 12 | Any middleware that you only wish to run in development mode should be added inside the `wrap-dev` function. 13 | This middleware will only be invoked when the environment contains the `:dev` key with a truthy value. 14 | 15 | ### Here are some links to get started 16 | 17 | 1. [HTML templating](http://www.luminusweb.net/docs/html_templating.md) 18 | 2. [Accessing the database](http://www.luminusweb.net/docs/database.md) 19 | 3. [Serving static resources](http://www.luminusweb.net/docs/static_resources.md) 20 | 4. [Setting response types](http://www.luminusweb.net/docs/responses.md) 21 | 5. [Defining routes](http://www.luminusweb.net/docs/routes.md) 22 | 6. [Adding middleware](http://www.luminusweb.net/docs/middleware.md) 23 | 7. [Sessions and cookies](http://www.luminusweb.net/docs/sessions_cookies.md) 24 | 8. [Security](http://www.luminusweb.net/docs/security.md) 25 | 9. [Deploying the application](http://www.luminusweb.net/docs/deployment.md) 26 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/public/css/screen.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 4 | height: 100%; 5 | padding-top: 40px; 6 | } 7 | {% if cljs %} 8 | @-moz-keyframes three-quarters-loader { 9 | 0% { 10 | -moz-transform: rotate(0deg); 11 | transform: rotate(0deg); 12 | } 13 | 100% { 14 | -moz-transform: rotate(360deg); 15 | transform: rotate(360deg); 16 | } 17 | } 18 | @-webkit-keyframes three-quarters-loader { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(360deg); 25 | transform: rotate(360deg); 26 | } 27 | } 28 | @keyframes three-quarters-loader { 29 | 0% { 30 | -moz-transform: rotate(0deg); 31 | -ms-transform: rotate(0deg); 32 | -webkit-transform: rotate(0deg); 33 | transform: rotate(0deg); 34 | } 35 | 100% { 36 | -moz-transform: rotate(360deg); 37 | -ms-transform: rotate(360deg); 38 | -webkit-transform: rotate(360deg); 39 | transform: rotate(360deg); 40 | } 41 | } 42 | /* :not(:required) hides this rule from IE9 and below */ 43 | .three-quarters-loader:not(:required) { 44 | -moz-animation: three-quarters-loader 1250ms infinite linear; 45 | -webkit-animation: three-quarters-loader 1250ms infinite linear; 46 | animation: three-quarters-loader 1250ms infinite linear; 47 | border: 8px solid #38e; 48 | border-right-color: transparent; 49 | border-radius: 16px; 50 | box-sizing: border-box; 51 | display: inline-block; 52 | position: relative; 53 | overflow: hidden; 54 | text-indent: -9999px; 55 | width: 32px; 56 | height: 32px; 57 | } 58 | {% endif %} 59 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |

this is the story of the Matangle usermanager.

4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to usermanager 7 | 8 | 9 | 10 | 40 | 41 |
42 | {% block content %} 43 | {% endblock %} 44 |
45 | 46 | {% style "/assets/bootstrap/css/bootstrap.min.css" %} 47 | {% style "/assets/bootstrap/css/bootstrap-theme.min.css" %} 48 | {% style "/css/screen.css" %} 49 | 50 | {% script "/assets/jquery/jquery.min.js" %} 51 | {% script "/assets/bootstrap/js/bootstrap.min.js" %} 52 | {% script "/assets/bootstrap/js/collapse.js" %} 53 | 54 | 57 | {% block page-scripts %} 58 | {% endblock %} 59 | 60 | 61 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Something bad happened 5 | 6 | 7 | {% style "/assets/bootstrap/css/bootstrap.min.css" %} 8 | {% style "/assets/bootstrap/css/bootstrap-theme.min.css" %} 9 | 35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 |

Error: {{status}}

43 |
44 | {% if title %} 45 |

{{title}}

46 | {% endif %} 47 | {% if message %} 48 |

{{message}}

49 | {% endif %} 50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Welcome to usermanager

5 |
6 | 7 |
8 |
9 |
11 |
12 |
14 |
15 |
17 | 18 |
19 | 20 |
21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/searchuser.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Search for User

5 |
6 | 7 |
8 |
9 |
10 |

11 | Name: 12 | 16 |

17 |

18 | Surname: 19 | 23 |

24 | 26 |
27 |
28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter03/crm1/resources/templates/useradd.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Add User

5 |
6 | 7 |
8 |
9 |
10 |

11 | Name: 12 | 16 |

17 |

18 | Surname: 19 | 23 |

24 | 26 |
27 |
28 |
29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /Chapter03/crm1/src/usermanager/core.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.core 2 | (:require [usermanager.handler :refer [app init destroy]] 3 | [immutant.web :as immutant] 4 | [clojure.tools.nrepl.server :as nrepl] 5 | [taoensso.timbre :as timbre] 6 | [environ.core :refer [env]]) 7 | (:gen-class)) 8 | 9 | (defonce nrepl-server (atom nil)) 10 | 11 | (defn parse-port [port] 12 | (when port 13 | (cond 14 | (string? port) (Integer/parseInt port) 15 | (number? port) port 16 | :else (throw (Exception. (str "invalid port value: " port)))))) 17 | 18 | (defn stop-nrepl [] 19 | (when-let [server @nrepl-server] 20 | (nrepl/stop-server server))) 21 | 22 | (defn start-nrepl 23 | "Start a network repl for debugging when the :nrepl-port is set in the environment." 24 | [] 25 | (if @nrepl-server 26 | (timbre/error "nREPL is already running!") 27 | (when-let [port (env :nrepl-port)] 28 | (try 29 | (->> port 30 | (parse-port) 31 | (nrepl/start-server :port) 32 | (reset! nrepl-server)) 33 | (timbre/info "nREPL server started on port" port) 34 | (catch Throwable t 35 | (timbre/error t "failed to start nREPL")))))) 36 | 37 | (defn http-port [port] 38 | (parse-port (or port (env :port) 3000))) 39 | 40 | (defonce http-server (atom nil)) 41 | 42 | (defn start-http-server [port] 43 | (init) 44 | (reset! http-server (immutant/run app :host "0.0.0.0" :port port))) 45 | 46 | (defn stop-http-server [] 47 | (when @http-server 48 | (destroy) 49 | (immutant/stop @http-server) 50 | (reset! http-server nil))) 51 | 52 | (defn stop-app [] 53 | (stop-nrepl) 54 | (stop-http-server) 55 | (shutdown-agents)) 56 | 57 | (defn start-app [[port]] 58 | (.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)) 59 | (start-nrepl) 60 | (start-http-server (http-port port)) 61 | (timbre/info "server started on port:" (:port @http-server))) 62 | 63 | (defn -main [& args] 64 | (start-app args)) 65 | -------------------------------------------------------------------------------- /Chapter03/crm1/src/usermanager/handler.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.handler 2 | (:require [compojure.core :refer [defroutes routes wrap-routes]] 3 | [usermanager.layout :refer [error-page]] 4 | [usermanager.routes.home :refer [home-routes]] 5 | [usermanager.middleware :as middleware] 6 | [compojure.route :as route] 7 | [taoensso.timbre :as timbre] 8 | [taoensso.timbre.appenders.3rd-party.rotor :as rotor] 9 | [selmer.parser :as parser] 10 | [environ.core :refer [env]])) 11 | 12 | (defn init 13 | "init will be called once when 14 | app is deployed as a servlet on 15 | an app server such as Tomcat 16 | put any initialization code here" 17 | [] 18 | 19 | (timbre/merge-config! 20 | {:level (if (env :dev) :trace :info) 21 | :appenders {:rotor (rotor/rotor-appender 22 | {:path "usermanager.log" 23 | :max-size (* 512 1024) 24 | :backlog 10})}}) 25 | 26 | (if (env :dev) (parser/cache-off!)) 27 | (timbre/info (str 28 | "\n-=[usermanager started successfully" 29 | (when (env :dev) " using the development profile") 30 | "]=-"))) 31 | 32 | (defn destroy 33 | "destroy will be called when your application 34 | shuts down, put any clean up code here" 35 | [] 36 | (timbre/info "usermanager is shutting down...") 37 | (timbre/info "shutdown complete!")) 38 | 39 | (def app-routes 40 | (routes 41 | (wrap-routes #'home-routes middleware/wrap-csrf) 42 | (route/not-found 43 | (:body 44 | (error-page {:status 404 45 | :title "page not found"}))))) 46 | 47 | (def app (middleware/wrap-base #'app-routes)) 48 | -------------------------------------------------------------------------------- /Chapter03/crm1/src/usermanager/layout.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.layout 2 | (:require [selmer.parser :as parser] 3 | [selmer.filters :as filters] 4 | [markdown.core :refer [md-to-html-string]] 5 | [ring.util.http-response :refer [content-type ok]] 6 | [ring.util.anti-forgery :refer [anti-forgery-field]] 7 | [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] 8 | [environ.core :refer [env]])) 9 | 10 | 11 | (declare ^:dynamic *app-context*) 12 | (parser/set-resource-path! (clojure.java.io/resource "templates")) 13 | (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) 14 | (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) 15 | 16 | (defn render 17 | "renders the HTML template located relative to resources/templates" 18 | [template & [params]] 19 | (content-type 20 | (ok 21 | (parser/render-file 22 | template 23 | (assoc params 24 | :page template 25 | :dev (env :dev) 26 | :csrf-token *anti-forgery-token* 27 | :servlet-context *app-context*))) 28 | "text/html; charset=utf-8")) 29 | 30 | (defn error-page 31 | "error-details should be a map containing the following keys: 32 | :status - error status 33 | :title - error title (optional) 34 | :message - detailed error message (optional) 35 | 36 | returns a response map with the error page as the body 37 | and the status specified by the status key" 38 | [error-details] 39 | {:status (:status error-details) 40 | :headers {"Content-Type" "text/html; charset=utf-8"} 41 | :body (parser/render-file "error.html" error-details)}) 42 | -------------------------------------------------------------------------------- /Chapter03/crm1/src/usermanager/middleware.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.middleware 2 | (:require [usermanager.layout :refer [*app-context* error-page]] 3 | [taoensso.timbre :as timbre] 4 | [environ.core :refer [env]] 5 | [selmer.middleware :refer [wrap-error-page]] 6 | [prone.middleware :refer [wrap-exceptions]] 7 | [ring.middleware.flash :refer [wrap-flash]] 8 | [immutant.web.middleware :refer [wrap-session]] 9 | [ring.middleware.reload :as reload] 10 | [ring.middleware.webjars :refer [wrap-webjars]] 11 | [ring.middleware.defaults :refer [site-defaults wrap-defaults]] 12 | [ring.middleware.anti-forgery :refer [wrap-anti-forgery]] 13 | [ring.middleware.format :refer [wrap-restful-format]]) 14 | (:import [javax.servlet ServletContext])) 15 | 16 | (defn wrap-context [handler] 17 | (fn [request] 18 | (binding [*app-context* 19 | (if-let [context (:servlet-context request)] 20 | ;; If we're not inside a servlet environment 21 | ;; (for example when using mock requests), then 22 | ;; .getContextPath might not exist 23 | (try (.getContextPath ^ServletContext context) 24 | (catch IllegalArgumentException _ context)) 25 | ;; if the context is not specified in the request 26 | ;; we check if one has been specified in the environment 27 | ;; instead 28 | (:app-context env))] 29 | (handler request)))) 30 | 31 | (defn wrap-internal-error [handler] 32 | (fn [req] 33 | (try 34 | (handler req) 35 | (catch Throwable t 36 | (timbre/error t) 37 | (error-page {:status 500 38 | :title "Something very bad has happened!" 39 | :message "We've dispatched a team of highly trained gnomes to take care of the problem."}))))) 40 | 41 | (defn wrap-dev [handler] 42 | (if (env :dev) 43 | (-> handler 44 | reload/wrap-reload 45 | wrap-error-page 46 | wrap-exceptions) 47 | handler)) 48 | 49 | (defn wrap-csrf [handler] 50 | (wrap-anti-forgery 51 | handler 52 | {:error-response 53 | (error-page 54 | {:status 403 55 | :title "Invalid anti-forgery token"})})) 56 | 57 | (defn wrap-formats [handler] 58 | (wrap-restful-format handler {:formats [:json-kw :transit-json :transit-msgpack]})) 59 | 60 | (defn wrap-base [handler] 61 | (-> handler 62 | wrap-dev 63 | wrap-formats 64 | wrap-webjars 65 | wrap-flash 66 | (wrap-session {:cookie-attrs {:http-only true}}) 67 | (wrap-defaults 68 | (-> site-defaults 69 | (assoc-in [:security :anti-forgery] false) 70 | (dissoc :session))) 71 | wrap-context 72 | wrap-internal-error)) 73 | -------------------------------------------------------------------------------- /Chapter03/crm1/src/usermanager/routes/home.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.routes.home 2 | (:require [usermanager.layout :as layout] 3 | [compojure.core :refer [defroutes GET]] 4 | [ring.util.http-response :refer [ok]] 5 | [clojure.java.io :as io])) 6 | 7 | (defn home-page [] 8 | (layout/render 9 | "home.html" {:docs (-> "docs/docs.md" io/resource slurp)})) 10 | 11 | (defn about-page [] 12 | (layout/render "about.html")) 13 | 14 | (defroutes home-routes 15 | (GET "/" [] (home-page)) 16 | (GET "/useradd" [] (layout/render "useradd.html")) 17 | (GET "/searchuser" [] (layout/render "searchuser.html")) 18 | (GET "/about" [] (about-page))) 19 | 20 | -------------------------------------------------------------------------------- /Chapter03/crm1/test/usermanager/test/handler.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.test.handler 2 | (:require [clojure.test :refer :all] 3 | [ring.mock.request :refer :all] 4 | [usermanager.handler :refer :all])) 5 | 6 | (deftest test-app 7 | (testing "main route" 8 | (let [response (app (request :get "/"))] 9 | (is (= 200 (:status response))))) 10 | 11 | (testing "not-found route" 12 | (let [response (app (request :get "/invalid"))] 13 | (is (= 404 (:status response)))))) 14 | -------------------------------------------------------------------------------- /Chapter03/liquibase-helloworld/README.org: -------------------------------------------------------------------------------- 1 | * Liquibase hello-world 2 | 3 | This is a simple hello-world style example for the Liquibase 4 | relational database changeset handler. 5 | 6 | ** Trying it out 7 | To try it out: 8 | 9 | #+BEGIN_SRC sh 10 | git clone git://github.com/jave/liquibase-helloworld.git 11 | cd liquibase-helloworld 12 | mvn liquibase:update 13 | #+END_SRC 14 | 15 | ** The changelog file 16 | A H2 database file /tmp/liquidhello.h2.db will be created. 17 | 18 | Below is an example of what Liquibase changelog file can look like. 19 | 20 | It defines two changesets, with ids 1 and 2. 21 | 22 | Changeset 1 creates a table called test, with a column called name. 23 | 24 | Changeset 2 adds a column to the table called test, called address. 25 | 26 | #+BEGIN_SRC xml 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | #+END_SRC 50 | 51 | ** The pom.xml file 52 | The pom.xml file defines things like the jdbc url we need, and also 53 | the version of the liquibase plugin. 54 | 55 | #+BEGIN_SRC xml 56 | 57 | 58 | 60 | 4.0.0 61 | se.verona.liquibasehello 62 | liquibasehello 63 | 1.0-SNAPSHOT 64 | 65 | 66 | 67 | org.liquibase 68 | liquibase-maven-plugin 69 | 3.0.0-rc1 70 | 71 | src/main/resources/db-changelog.xml 72 | org.h2.Driver 73 | jdbc:h2:liquidhello 74 | 75 | 76 | 77 | com.h2database 78 | h2 79 | 1.3.171 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | #+END_SRC 88 | -------------------------------------------------------------------------------- /Chapter03/liquibase-helloworld/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | se.verona.liquibasehello 6 | liquibasehello 7 | 1.0-SNAPSHOT 8 | 9 | 10 | 11 | org.liquibase 12 | liquibase-maven-plugin 13 | 3.0.0-rc1 14 | 15 | src/main/resources/db-changelog.xml 16 | org.h2.Driver 17 | jdbc:h2:liquidhello 18 | 19 | 20 | 21 | com.h2database 22 | h2 23 | 1.3.171 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Chapter03/liquibase-helloworld/src/main/resources/db-changelog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Chapter06/autotest_v1/bin/autotest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd `dirname $0` 3 | cd .. 4 | mvn clean test 5 | -------------------------------------------------------------------------------- /Chapter06/autotest_v1/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | 8 | practdev 9 | autotest-v1 10 | 1.0-SNAPSHOT 11 | jar 12 | 13 | 14 | 15 | 4.12 16 | 1.2.17 17 | 2.50.1 18 | 1.2.4 19 | UTF-8 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | ${junit.version} 27 | test 28 | jar 29 | 30 | 31 | 32 | log4j 33 | log4j 34 | ${log4j.version} 35 | jar 36 | 37 | 38 | 39 | org.seleniumhq.selenium 40 | selenium-java 41 | ${selenium.version} 42 | 43 | 44 | 45 | info.cukes 46 | cucumber-core 47 | ${cucumber.version} 48 | 49 | 50 | 51 | info.cukes 52 | cucumber-java 53 | ${cucumber.version} 54 | 55 | 56 | 57 | info.cukes 58 | cucumber-junit 59 | ${cucumber.version} 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Chapter06/autotest_v1/src/test/java/practdev/UserManagerSeleniumTest.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | import org.junit.After; 3 | import org.junit.AfterClass; 4 | import org.junit.Before; 5 | import org.junit.BeforeClass; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.JUnit4; 9 | import org.openqa.selenium.By; 10 | import org.openqa.selenium.WebDriver; 11 | import org.openqa.selenium.WebElement; 12 | import org.openqa.selenium.firefox.FirefoxDriver; 13 | import static org.junit.Assert.*; 14 | @RunWith(JUnit4.class) 15 | public class UserManagerSeleniumTest { 16 | private static WebDriver webDriver = null; 17 | @BeforeClass 18 | public static void beforeClass(){ 19 | //This starts FireFox 20 | webDriver = new FirefoxDriver(); 21 | } 22 | @Before 23 | public void beforeTestCaseMethod() throws InterruptedException{ 24 | //This points FireFox to the usermanager example, 25 | // for this to work the sample needs to run first 26 | webDriver.get("http://localhost:3000/useradd"); 27 | Thread.sleep(3000); 28 | } 29 | @Test 30 | public void testUserAdd() throws InterruptedException{ 31 | //Now we find the name and surname boxes 32 | WebElement nameBox = webDriver.findElement(By.xpath("//input[@name='name']")); 33 | WebElement surnameBox = webDriver.findElement(By.xpath("//input[@name='surname']")); 34 | //Now we submit our query 35 | nameBox.sendKeys("Alan"); 36 | surnameBox.sendKeys("Turing"); 37 | surnameBox.submit();//submit the form 38 | nameBox = webDriver.findElement(By.xpath("//div[@name='name']")); 39 | surnameBox = webDriver.findElement(By.xpath("//div[@name='surname']")); 40 | System.out.println(nameBox.getText() + " " + surnameBox.getText()); 41 | assertEquals(nameBox.getText() + " " + surnameBox.getText(), "Alan Turing"); 42 | Thread.sleep(5000); 43 | } 44 | 45 | @After 46 | public void afterTestCaseMethod() throws InterruptedException{ 47 | Thread.sleep(2000); 48 | } 49 | 50 | @AfterClass 51 | public static void afterClass(){ 52 | //clean up 53 | webDriver.quit(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Chapter06/autotest_v1/src/test/resources/features/manageusers.feature: -------------------------------------------------------------------------------- 1 | Feature: Manage users 2 | As an Administrator 3 | I want to be able to 4 | - Create a user 5 | - Search for the user 6 | - Delete the user 7 | 8 | Scenario: Create a named user 9 | Given a user with the name 'Alan' 10 | And the surname 'Turing' 11 | When the administrator clicks 'Add User' 12 | Then the user should be added 13 | 14 | Scenario: Search for a named user 15 | Given a user with the name 'Alan' 16 | And the surname 'Turing' 17 | When the administrator clicks 'Search for User' 18 | Then the user 'Alan Turing' should be shown 19 | 20 | Scenario: Delete a named user 21 | Given a user with the name 'Alan' 22 | And the surname 'Turing' 23 | When the administrator clicks 'Delete User' 24 | Then the user should be deleted 25 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/README.md: -------------------------------------------------------------------------------- 1 | # Cucumber-Java-JUnit Archetype 2 | 3 | This is the simplest possible build script setup for Cucumber using Java. 4 | There is nothing fancy like a webapp or browser testing. All this does is to show you how 5 | to install and run Cucumber! 6 | 7 | ## Usage 8 | 9 | Open a command window and run: 10 | 11 | mvn test 12 | 13 | This runs Cucumber features using Cucumber's JUnit runner. The `@RunWith(Cucumber.class)` annotation on the `RunCukesTest` 14 | class tells JUnit to kick off Cucumber. 15 | 16 | ## Overriding options 17 | 18 | The Cucumber runtime parses command line options to know what features to run, where the glue code lives, what plugins to use etc. 19 | When you use the JUnit runner, these options are generated from the `@CucumberOptions` annotation on your test. 20 | 21 | Sometimes it can be useful to override these options without changing or recompiling the JUnit class. This can be done with the 22 | `cucumber.options` system property. The general form is: 23 | 24 | mvn -Dcucumber.options="..." test 25 | 26 | Let's look at some things you can do with `cucumber.options`. Try this: 27 | 28 | -Dcucumber.options="--help" 29 | 30 | That should list all the available options. 31 | 32 | *IMPORTANT* 33 | 34 | When you override options with `-Dcucumber.options`, you will completely override whatever options are hard-coded in 35 | your `@CucumberOptions` or in the script calling `cucumber.api.cli.Main`. There is one exception to this rule, and that 36 | is the `--plugin` option. This will not _override_, but _add_ a plugin. The reason for this is to make it easier 37 | for 3rd party tools (such as [Cucumber Pro](https://cucumber.pro/)) to automatically configure additional plugins by appending arguments to a `cucumber.properties` 38 | file. 39 | 40 | ### Run a subset of Features or Scenarios 41 | 42 | Specify a particular scenario by *line* (and use the pretty plugin, which prints the scenario back) 43 | 44 | -Dcucumber.options="classpath:skeleton/belly.feature:4 --plugin pretty" 45 | 46 | This works because Maven puts `./src/test/resources` on your `classpath`. 47 | You can also specify files to run by filesystem path: 48 | 49 | -Dcucumber.options="src/test/resources/skeleton/belly.feature:4 --plugin pretty" 50 | 51 | You can also specify what to run by *tag*: 52 | 53 | -Dcucumber.options="--tags @bar --plugin pretty" 54 | 55 | ### Running only the scenarios that failed in the previous run 56 | 57 | -Dcucumber.options="@target/rerun.txt" 58 | 59 | This works as long as you have the `rerun` formatter enabled. 60 | 61 | ### Specify a different formatter: 62 | 63 | For example a JUnit formatter: 64 | 65 | -Dcucumber.options="--plugin junit:target/cucumber-junit-report.xml" 66 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | practdev 7 | hello-cucumber6 8 | 1.0-SNAPSHOT 9 | jar 10 | Cucumber-Java-JUnit Project 11 | 12 | 13 | 14 | info.cukes 15 | cucumber-java 16 | 1.2.4 17 | test 18 | 19 | 20 | 21 | info.cukes 22 | cucumber-junit 23 | 1.2.4 24 | test 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.12 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 3.2 41 | 42 | UTF-8 43 | 1.8 44 | 1.8 45 | -Werror 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/src/main/java/practdev/Calculator.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | public class Calculator { 4 | public void push(Integer num) { 5 | 6 | } 7 | 8 | public void op(String op) { 9 | 10 | } 11 | 12 | public Integer result() { 13 | return 0; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/src/test/java/practdev/CalculatorSteps.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | import cucumber.api.java.en.*; 4 | import static org.junit.Assert.*; 5 | public class CalculatorSteps{ 6 | 7 | Calculator calc; 8 | 9 | @Given("^I have entered (\\d+) into the calculator$") 10 | public void a(Integer i){ 11 | System.out.format("Number entered: %n\n", i); 12 | // calc.push(i); 13 | }; 14 | 15 | @Given("^I press (\\w+)") 16 | public void b(String op){ 17 | System.out.format("operator entered: %n\n", op); 18 | // calc.op(op); 19 | }; 20 | @Then("The result should be (\\d+)") 21 | public void c(Integer i){ 22 | System.out.format("result : %n\n", i); 23 | // assertThat(calc.result(),i); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/src/test/java/practdev/CalculatorTest.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | import cucumber.api.CucumberOptions; 4 | import cucumber.api.junit.Cucumber; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | public class CalculatorTest { 9 | } 10 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber6/src/test/resources/practdev/addition.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition 2 | I would like to add numbers with my pocket calculator 3 | 4 | Scenario: Integer numbers 5 | * I have entered 4 into the calculator 6 | * I press add 7 | * I have entered 2 into the calculator 8 | * I press equal 9 | * The result should be 6 on the screen 10 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/README.md: -------------------------------------------------------------------------------- 1 | # Cucumber-Java-JUnit Archetype 2 | 3 | This is the simplest possible build script setup for Cucumber using Java. 4 | There is nothing fancy like a webapp or browser testing. All this does is to show you how 5 | to install and run Cucumber! 6 | 7 | ## Usage 8 | 9 | Open a command window and run: 10 | 11 | mvn test 12 | 13 | This runs Cucumber features using Cucumber's JUnit runner. The `@RunWith(Cucumber.class)` annotation on the `RunCukesTest` 14 | class tells JUnit to kick off Cucumber. 15 | 16 | ## Overriding options 17 | 18 | The Cucumber runtime parses command line options to know what features to run, where the glue code lives, what plugins to use etc. 19 | When you use the JUnit runner, these options are generated from the `@CucumberOptions` annotation on your test. 20 | 21 | Sometimes it can be useful to override these options without changing or recompiling the JUnit class. This can be done with the 22 | `cucumber.options` system property. The general form is: 23 | 24 | mvn -Dcucumber.options="..." test 25 | 26 | Let's look at some things you can do with `cucumber.options`. Try this: 27 | 28 | -Dcucumber.options="--help" 29 | 30 | That should list all the available options. 31 | 32 | *IMPORTANT* 33 | 34 | When you override options with `-Dcucumber.options`, you will completely override whatever options are hard-coded in 35 | your `@CucumberOptions` or in the script calling `cucumber.api.cli.Main`. There is one exception to this rule, and that 36 | is the `--plugin` option. This will not _override_, but _add_ a plugin. The reason for this is to make it easier 37 | for 3rd party tools (such as [Cucumber Pro](https://cucumber.pro/)) to automatically configure additional plugins by appending arguments to a `cucumber.properties` 38 | file. 39 | 40 | ### Run a subset of Features or Scenarios 41 | 42 | Specify a particular scenario by *line* (and use the pretty plugin, which prints the scenario back) 43 | 44 | -Dcucumber.options="classpath:skeleton/belly.feature:4 --plugin pretty" 45 | 46 | This works because Maven puts `./src/test/resources` on your `classpath`. 47 | You can also specify files to run by filesystem path: 48 | 49 | -Dcucumber.options="src/test/resources/skeleton/belly.feature:4 --plugin pretty" 50 | 51 | You can also specify what to run by *tag*: 52 | 53 | -Dcucumber.options="--tags @bar --plugin pretty" 54 | 55 | ### Running only the scenarios that failed in the previous run 56 | 57 | -Dcucumber.options="@target/rerun.txt" 58 | 59 | This works as long as you have the `rerun` formatter enabled. 60 | 61 | ### Specify a different formatter: 62 | 63 | For example a JUnit formatter: 64 | 65 | -Dcucumber.options="--plugin junit:target/cucumber-junit-report.xml" 66 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | practdev 7 | hello-cucumber 8 | 1.0-SNAPSHOT 9 | jar 10 | Cucumber-Java-JUnit Project 11 | 12 | 13 | 14 | info.cukes 15 | cucumber-java8 16 | 1.2.4 17 | test 18 | 19 | 20 | 21 | info.cukes 22 | cucumber-junit 23 | 1.2.4 24 | test 25 | 26 | 27 | 28 | junit 29 | junit 30 | 4.12 31 | test 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 3.2 41 | 42 | UTF-8 43 | 1.8 44 | 1.8 45 | -Werror 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/src/main/java/practdev/Calculator.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | public class Calculator { 4 | public void push(Integer num) { 5 | 6 | } 7 | 8 | public void op(String op) { 9 | 10 | } 11 | 12 | public Integer result() { 13 | return 0; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/src/test/java/practdev/CalculatorSteps.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | import cucumber.api.java8.En; 4 | import static org.junit.Assert.*; 5 | public class CalculatorSteps implements En{ 6 | 7 | Calculator calc; 8 | public void CalculatorSteps() { 9 | Given("^I have entered (\\d+) into the calculator$", (Integer i) -> { 10 | System.out.format("Number entered: %n\n", i); 11 | calc.push(i); 12 | }); 13 | 14 | Given("^I press add$", (String op) -> { 15 | System.out.format("operator entered: %n\n", op); 16 | calc.op(op); 17 | }); 18 | Then("The result should be (\\d+)", (Integer i) -> { 19 | System.out.format("result : %n\n", i); 20 | // assertThat(calc.result(),i); 21 | }); 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/src/test/java/practdev/CalculatorTest.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | import cucumber.api.CucumberOptions; 4 | import cucumber.api.junit.Cucumber; 5 | import org.junit.runner.RunWith; 6 | 7 | @RunWith(Cucumber.class) 8 | public class CalculatorTest { 9 | } 10 | -------------------------------------------------------------------------------- /Chapter06/hello-cucumber8/src/test/resources/practdev/addition.feature: -------------------------------------------------------------------------------- 1 | Feature: Addition 2 | I would like to add numbers with my pocket calculator 3 | 4 | Scenario: Integer numbers 5 | * I have entered 4 into the calculator 6 | * I press add 7 | * I have entered 2 into the calculator 8 | * I press equal 9 | * The result should be 6 on the screen 10 | -------------------------------------------------------------------------------- /Chapter06/hello-junit/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | practdev 6 | hello-junit 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | hello-junit 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | junit 20 | junit 21 | 4.12 22 | test 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter06/hello-junit/src/main/java/practdev/App.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | /** 4 | * Hello world! 5 | * 6 | */ 7 | public class App 8 | { 9 | public static void main( String[] args ) 10 | { 11 | System.out.println( "Hello World!" ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter06/hello-junit/src/test/java/practdev/AppTest.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Chapter06/hello-selenium/pom.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 4.0.0 7 | 8 | practdev 9 | hello-selenium 10 | 1.0-SNAPSHOT 11 | jar 12 | 13 | 14 | 15 | 4.12 16 | 1.2.17 17 | 2.50.1 18 | UTF-8 19 | 20 | 21 | 22 | 23 | junit 24 | junit 25 | ${junit.version} 26 | test 27 | jar 28 | 29 | 30 | 31 | log4j 32 | log4j 33 | ${log4j.version} 34 | jar 35 | 36 | 37 | 38 | org.seleniumhq.selenium 39 | selenium-java 40 | ${selenium.version} 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Chapter06/hello-selenium/src/test/java/practdev/HelloSeleniumTest.java: -------------------------------------------------------------------------------- 1 | package practdev; 2 | import org.junit.After; 3 | import org.junit.AfterClass; 4 | import org.junit.Before; 5 | import org.junit.BeforeClass; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.JUnit4; 9 | import org.openqa.selenium.By; 10 | import org.openqa.selenium.WebDriver; 11 | import org.openqa.selenium.WebElement; 12 | import org.openqa.selenium.firefox.FirefoxDriver; 13 | @RunWith(JUnit4.class) 14 | public class HelloSeleniumTest { 15 | private static WebDriver webDriver = null; 16 | @BeforeClass 17 | public static void beforeClass(){ 18 | //This starts FireFox 19 | webDriver = new FirefoxDriver(); 20 | } 21 | @Before 22 | public void beforeTestCaseMethod() throws InterruptedException{ 23 | //This points FireFox to google 24 | webDriver.get("http://www.google.com"); 25 | Thread.sleep(3000); 26 | } 27 | @Test 28 | public void testHelloWorldSearch() throws InterruptedException{ 29 | //Now we find the search box 30 | WebElement googleSearchBox = webDriver.findElement(By.xpath("//input[@name='q']")); 31 | //Now we submit our query 32 | googleSearchBox.sendKeys("Hello World\n"); 33 | googleSearchBox.submit(); 34 | Thread.sleep(5000); 35 | } 36 | 37 | @After 38 | public void afterTestCaseMethod() throws InterruptedException{ 39 | Thread.sleep(2000); 40 | } 41 | 42 | @AfterClass 43 | public static void afterClass(){ 44 | //clean up 45 | webDriver.quit(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Chapter06/usermanager/profiles.clj: -------------------------------------------------------------------------------- 1 | {:profiles/dev {:env {}} 2 | :profiles/test {:env {}}} 3 | -------------------------------------------------------------------------------- /Chapter06/usermanager/project.clj: -------------------------------------------------------------------------------- 1 | (defproject usermanager "0.1.0-SNAPSHOT" 2 | 3 | :description "FIXME: write description" 4 | :url "http://example.com/FIXME" 5 | 6 | :dependencies [[org.clojure/clojure "1.7.0"] 7 | [selmer "0.9.2"] 8 | [com.taoensso/timbre "4.1.2"] 9 | [com.taoensso/tower "3.0.2"] 10 | [markdown-clj "0.9.74"] 11 | [environ "1.0.1"] 12 | [compojure "1.4.0"] 13 | [ring-webjars "0.1.1"] 14 | [ring/ring-defaults "0.1.5"] 15 | [ring "1.4.0" 16 | :exclusions [ring/ring-jetty-adapter]] 17 | [metosin/ring-middleware-format "0.6.0"] 18 | [metosin/ring-http-response "0.6.5"] 19 | [bouncer "0.3.3"] 20 | [prone "0.8.2"] 21 | [org.clojure/tools.nrepl "0.2.11"] 22 | [org.webjars/bootstrap "3.3.5"] 23 | [org.webjars/jquery "2.1.4"] 24 | [org.immutant/web "2.1.0"]] 25 | 26 | :min-lein-version "2.0.0" 27 | :uberjar-name "usermanager.jar" 28 | :jvm-opts ["-server"] 29 | 30 | :main usermanager.core 31 | 32 | :plugins [[lein-environ "1.0.1"]] 33 | :profiles 34 | {:uberjar {:omit-source true 35 | :env {:production true} 36 | :aot :all} 37 | :dev [:project/dev :profiles/dev] 38 | :test [:project/test :profiles/test] 39 | :project/dev {:dependencies [[ring/ring-mock "0.3.0"] 40 | [ring/ring-devel "1.4.0"] 41 | [pjstadig/humane-test-output "0.7.0"]] 42 | 43 | 44 | :repl-options {:init-ns usermanager.core} 45 | :injections [(require 'pjstadig.humane-test-output) 46 | (pjstadig.humane-test-output/activate!)] 47 | ;;when :nrepl-port is set the application starts the nREPL server on load 48 | :env {:dev true 49 | :port 3000 50 | :nrepl-port 7000}} 51 | :project/test {:env {:test true 52 | :port 3001 53 | :nrepl-port 7001}} 54 | :profiles/dev {} 55 | :profiles/test {}}) 56 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/docs/docs.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Managing Your Middleware 4 | 5 | Request middleware functions are located under the `usermanager.middleware` namespace. 6 | 7 | This namespace is reserved for any custom middleware for the application. Some default middleware is 8 | already defined here. 9 | 10 | The two function for organizing the middleware are called `wrap-dev` and `wrap-base`. 11 | 12 | Any middleware that you only wish to run in development mode should be added inside the `wrap-dev` function. 13 | This middleware will only be invoked when the environment contains the `:dev` key with a truthy value. 14 | 15 | ### Here are some links to get started 16 | 17 | 1. [HTML templating](http://www.luminusweb.net/docs/html_templating.md) 18 | 2. [Accessing the database](http://www.luminusweb.net/docs/database.md) 19 | 3. [Serving static resources](http://www.luminusweb.net/docs/static_resources.md) 20 | 4. [Setting response types](http://www.luminusweb.net/docs/responses.md) 21 | 5. [Defining routes](http://www.luminusweb.net/docs/routes.md) 22 | 6. [Adding middleware](http://www.luminusweb.net/docs/middleware.md) 23 | 7. [Sessions and cookies](http://www.luminusweb.net/docs/sessions_cookies.md) 24 | 8. [Security](http://www.luminusweb.net/docs/security.md) 25 | 9. [Deploying the application](http://www.luminusweb.net/docs/deployment.md) 26 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/public/css/screen.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 4 | height: 100%; 5 | padding-top: 40px; 6 | } 7 | {% if cljs %} 8 | @-moz-keyframes three-quarters-loader { 9 | 0% { 10 | -moz-transform: rotate(0deg); 11 | transform: rotate(0deg); 12 | } 13 | 100% { 14 | -moz-transform: rotate(360deg); 15 | transform: rotate(360deg); 16 | } 17 | } 18 | @-webkit-keyframes three-quarters-loader { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(360deg); 25 | transform: rotate(360deg); 26 | } 27 | } 28 | @keyframes three-quarters-loader { 29 | 0% { 30 | -moz-transform: rotate(0deg); 31 | -ms-transform: rotate(0deg); 32 | -webkit-transform: rotate(0deg); 33 | transform: rotate(0deg); 34 | } 35 | 100% { 36 | -moz-transform: rotate(360deg); 37 | -ms-transform: rotate(360deg); 38 | -webkit-transform: rotate(360deg); 39 | transform: rotate(360deg); 40 | } 41 | } 42 | /* :not(:required) hides this rule from IE9 and below */ 43 | .three-quarters-loader:not(:required) { 44 | -moz-animation: three-quarters-loader 1250ms infinite linear; 45 | -webkit-animation: three-quarters-loader 1250ms infinite linear; 46 | animation: three-quarters-loader 1250ms infinite linear; 47 | border: 8px solid #38e; 48 | border-right-color: transparent; 49 | border-radius: 16px; 50 | box-sizing: border-box; 51 | display: inline-block; 52 | position: relative; 53 | overflow: hidden; 54 | text-indent: -9999px; 55 | width: 32px; 56 | height: 32px; 57 | } 58 | {% endif %} 59 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |

this is the story of the Matangle usermanager.

4 | {% endblock %} 5 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to usermanager 7 | 8 | 9 | 10 | 40 | 41 |
42 | {% block content %} 43 | {% endblock %} 44 |
45 | 46 | {% style "/assets/bootstrap/css/bootstrap.min.css" %} 47 | {% style "/assets/bootstrap/css/bootstrap-theme.min.css" %} 48 | {% style "/css/screen.css" %} 49 | 50 | {% script "/assets/jquery/jquery.min.js" %} 51 | {% script "/assets/bootstrap/js/bootstrap.min.js" %} 52 | {% script "/assets/bootstrap/js/collapse.js" %} 53 | 54 | 57 | {% block page-scripts %} 58 | {% endblock %} 59 | 60 | 61 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Something bad happened 5 | 6 | 7 | {% style "/assets/bootstrap/css/bootstrap.min.css" %} 8 | {% style "/assets/bootstrap/css/bootstrap-theme.min.css" %} 9 | 35 | 36 | 37 |
38 |
39 |
40 |
41 |
42 |

Error: {{status}}

43 |
44 | {% if title %} 45 |

{{title}}

46 | {% endif %} 47 | {% if message %} 48 |

{{message}}

49 | {% endif %} 50 |
51 |
52 |
53 |
54 |
55 | 56 | 57 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Welcome to usermanager

5 |
6 | 7 |
8 |
9 |
12 |
13 |
16 |
17 |
20 |
21 |
22 | 23 |
24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/searchresult.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Matching Users

5 |
6 | 7 | {% for user in users %} 8 |
9 |
10 | Name: 11 | value="{{user.name}}" 12 |
13 |
14 |
15 |
16 | Surname: 17 | value="{{user.surname}}" 18 |
19 |
20 | {% endfor %} 21 | {% endblock %} 22 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/searchuser.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Search for User

5 |
6 | 7 |
8 |
9 |
10 | {% csrf-field %} 11 |

12 | Name: 13 | 17 |

18 |

19 | Surname: 20 | 24 |

25 | 27 |
28 |
29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/useradd.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Add User

5 |
6 | 7 |
8 |
9 |
10 | {% csrf-field %} 11 |

12 | Name: 13 | 17 |

18 |

19 | Surname: 20 | 24 |

25 | 27 |
28 |
29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/useradded.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Added User

5 |
6 | 7 |
8 |
9 | {{name}} 10 |
11 |
12 |
13 |
14 | {{surname}} 15 |
16 | added. 17 |
18 | {% endblock %} 19 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/userdelete.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Delete User

5 |
6 | 7 |
8 |
9 |
10 | {% csrf-field %} 11 |

12 | Name: 13 | 17 |

18 |

19 | Surname: 20 | 24 |

25 | 27 |
28 |
29 |
30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /Chapter06/usermanager/resources/templates/userdeleted.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block content %} 3 |
4 |

Deleted User

5 |
6 | 7 |
8 |
9 | Name: 10 | value="{{name}}" 11 |
12 |
13 |
14 |
15 | Surname: 16 | value="{{surname}}" 17 |
18 |
19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /Chapter06/usermanager/src/usermanager/core.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.core 2 | (:require [usermanager.handler :refer [app init destroy]] 3 | [immutant.web :as immutant] 4 | [clojure.tools.nrepl.server :as nrepl] 5 | [taoensso.timbre :as timbre] 6 | [environ.core :refer [env]]) 7 | (:gen-class)) 8 | 9 | (defonce nrepl-server (atom nil)) 10 | 11 | (defn parse-port [port] 12 | (when port 13 | (cond 14 | (string? port) (Integer/parseInt port) 15 | (number? port) port 16 | :else (throw (Exception. (str "invalid port value: " port)))))) 17 | 18 | (defn stop-nrepl [] 19 | (when-let [server @nrepl-server] 20 | (nrepl/stop-server server))) 21 | 22 | (defn start-nrepl 23 | "Start a network repl for debugging when the :nrepl-port is set in the environment." 24 | [] 25 | (if @nrepl-server 26 | (timbre/error "nREPL is already running!") 27 | (when-let [port (env :nrepl-port)] 28 | (try 29 | (->> port 30 | (parse-port) 31 | (nrepl/start-server :port) 32 | (reset! nrepl-server)) 33 | (timbre/info "nREPL server started on port" port) 34 | (catch Throwable t 35 | (timbre/error t "failed to start nREPL")))))) 36 | 37 | (defn http-port [port] 38 | (parse-port (or port (env :port) 3000))) 39 | 40 | (defonce http-server (atom nil)) 41 | 42 | (defn start-http-server [port] 43 | (init) 44 | (reset! http-server (immutant/run app :host "0.0.0.0" :port port))) 45 | 46 | (defn stop-http-server [] 47 | (when @http-server 48 | (destroy) 49 | (immutant/stop @http-server) 50 | (reset! http-server nil))) 51 | 52 | (defn stop-app [] 53 | (stop-nrepl) 54 | (stop-http-server) 55 | (shutdown-agents)) 56 | 57 | (defn start-app [[port]] 58 | (.addShutdownHook (Runtime/getRuntime) (Thread. stop-app)) 59 | (start-nrepl) 60 | (start-http-server (http-port port)) 61 | (timbre/info "server started on port:" (:port @http-server))) 62 | 63 | (defn -main [& args] 64 | (start-app args)) 65 | -------------------------------------------------------------------------------- /Chapter06/usermanager/src/usermanager/handler.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.handler 2 | (:require [compojure.core :refer [defroutes routes wrap-routes]] 3 | [usermanager.layout :refer [error-page]] 4 | [usermanager.routes.home :refer [home-routes]] 5 | [usermanager.middleware :as middleware] 6 | [compojure.route :as route] 7 | [taoensso.timbre :as timbre] 8 | [taoensso.timbre.appenders.3rd-party.rotor :as rotor] 9 | [selmer.parser :as parser] 10 | [environ.core :refer [env]])) 11 | 12 | (defn init 13 | "init will be called once when 14 | app is deployed as a servlet on 15 | an app server such as Tomcat 16 | put any initialization code here" 17 | [] 18 | 19 | (timbre/merge-config! 20 | {:level (if (env :dev) :trace :info) 21 | :appenders {:rotor (rotor/rotor-appender 22 | {:path "usermanager.log" 23 | :max-size (* 512 1024) 24 | :backlog 10})}}) 25 | 26 | (if (env :dev) (parser/cache-off!)) 27 | (timbre/info (str 28 | "\n-=[usermanager started successfully" 29 | (when (env :dev) " using the development profile") 30 | "]=-"))) 31 | 32 | (defn destroy 33 | "destroy will be called when your application 34 | shuts down, put any clean up code here" 35 | [] 36 | (timbre/info "usermanager is shutting down...") 37 | (timbre/info "shutdown complete!")) 38 | 39 | (def app-routes 40 | (routes 41 | (wrap-routes #'home-routes middleware/wrap-csrf) 42 | (route/not-found 43 | (:body 44 | (error-page {:status 404 45 | :title "page not found"}))))) 46 | 47 | (def app (middleware/wrap-base #'app-routes)) 48 | -------------------------------------------------------------------------------- /Chapter06/usermanager/src/usermanager/layout.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.layout 2 | (:require [selmer.parser :as parser] 3 | [selmer.filters :as filters] 4 | [markdown.core :refer [md-to-html-string]] 5 | [ring.util.http-response :refer [content-type ok]] 6 | [ring.util.anti-forgery :refer [anti-forgery-field]] 7 | [ring.middleware.anti-forgery :refer [*anti-forgery-token*]] 8 | [environ.core :refer [env]])) 9 | 10 | 11 | (declare ^:dynamic *app-context*) 12 | (parser/set-resource-path! (clojure.java.io/resource "templates")) 13 | (parser/add-tag! :csrf-field (fn [_ _] (anti-forgery-field))) 14 | (filters/add-filter! :markdown (fn [content] [:safe (md-to-html-string content)])) 15 | 16 | (defn render 17 | "renders the HTML template located relative to resources/templates" 18 | [template & [params]] 19 | (content-type 20 | (ok 21 | (parser/render-file 22 | template 23 | (assoc params 24 | :page template 25 | :dev (env :dev) 26 | :csrf-token *anti-forgery-token* 27 | :servlet-context *app-context*))) 28 | "text/html; charset=utf-8")) 29 | 30 | (defn error-page 31 | "error-details should be a map containing the following keys: 32 | :status - error status 33 | :title - error title (optional) 34 | :message - detailed error message (optional) 35 | 36 | returns a response map with the error page as the body 37 | and the status specified by the status key" 38 | [error-details] 39 | {:status (:status error-details) 40 | :headers {"Content-Type" "text/html; charset=utf-8"} 41 | :body (parser/render-file "error.html" error-details)}) 42 | -------------------------------------------------------------------------------- /Chapter06/usermanager/src/usermanager/middleware.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.middleware 2 | (:require [usermanager.layout :refer [*app-context* error-page]] 3 | [taoensso.timbre :as timbre] 4 | [environ.core :refer [env]] 5 | [selmer.middleware :refer [wrap-error-page]] 6 | [prone.middleware :refer [wrap-exceptions]] 7 | [ring.middleware.flash :refer [wrap-flash]] 8 | [immutant.web.middleware :refer [wrap-session]] 9 | [ring.middleware.reload :as reload] 10 | [ring.middleware.webjars :refer [wrap-webjars]] 11 | [ring.middleware.defaults :refer [site-defaults wrap-defaults]] 12 | [ring.middleware.anti-forgery :refer [wrap-anti-forgery]] 13 | [ring.middleware.format :refer [wrap-restful-format]]) 14 | (:import [javax.servlet ServletContext])) 15 | 16 | (defn wrap-context [handler] 17 | (fn [request] 18 | (binding [*app-context* 19 | (if-let [context (:servlet-context request)] 20 | ;; If we're not inside a servlet environment 21 | ;; (for example when using mock requests), then 22 | ;; .getContextPath might not exist 23 | (try (.getContextPath ^ServletContext context) 24 | (catch IllegalArgumentException _ context)) 25 | ;; if the context is not specified in the request 26 | ;; we check if one has been specified in the environment 27 | ;; instead 28 | (:app-context env))] 29 | (handler request)))) 30 | 31 | (defn wrap-internal-error [handler] 32 | (fn [req] 33 | (try 34 | (handler req) 35 | (catch Throwable t 36 | (timbre/error t) 37 | (error-page {:status 500 38 | :title "Something very bad has happened!" 39 | :message "We've dispatched a team of highly trained gnomes to take care of the problem."}))))) 40 | 41 | (defn wrap-dev [handler] 42 | (if (env :dev) 43 | (-> handler 44 | reload/wrap-reload 45 | wrap-error-page 46 | wrap-exceptions) 47 | handler)) 48 | 49 | (defn wrap-csrf [handler] 50 | (wrap-anti-forgery 51 | handler 52 | {:error-response 53 | (error-page 54 | {:status 403 55 | :title "Invalid anti-forgery token"})})) 56 | 57 | (defn wrap-formats [handler] 58 | (wrap-restful-format handler {:formats [:json-kw :transit-json :transit-msgpack]})) 59 | 60 | (defn wrap-base [handler] 61 | (-> handler 62 | wrap-dev 63 | wrap-formats 64 | wrap-webjars 65 | wrap-flash 66 | (wrap-session {:cookie-attrs {:http-only true}}) 67 | (wrap-defaults 68 | (-> site-defaults 69 | (assoc-in [:security :anti-forgery] false) 70 | (dissoc :session))) 71 | wrap-context 72 | wrap-internal-error)) 73 | -------------------------------------------------------------------------------- /Chapter06/usermanager/src/usermanager/routes/home.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.routes.home 2 | (:require [usermanager.layout :as layout] 3 | [compojure.core :refer [defroutes GET POST]] 4 | [ring.util.http-response :refer [ok]] 5 | [clojure.java.io :as io])) 6 | 7 | (defn home-page [] 8 | (layout/render 9 | "home.html" {:docs (-> "docs/docs.md" io/resource slurp)})) 10 | 11 | (defn about-page [] 12 | (layout/render "about.html")) 13 | 14 | (def user-list (atom [])) 15 | 16 | (defn user-list-add [name surname] 17 | (swap! user-list conj {:name name :surname surname} )) 18 | 19 | (defn user-list-delete [name surname] 20 | (reset! user-list (remove (fn [ x] (= x {:surname surname :name name })) @user-list))) 21 | 22 | (defn useradded [name surname] 23 | (layout/render "useradded.html" {:name name :surname surname}) 24 | (user-list-add name surname) 25 | ) 26 | 27 | (defroutes home-routes 28 | (GET "/" [] (home-page)) 29 | (GET "/useradd" [] (layout/render "useradd.html")) 30 | (GET "/userdelete" [] (layout/render "userdelete.html")) 31 | (POST "/useradded" [name surname] (do (user-list-add name surname) (layout/render "useradded.html" {:name name :surname surname}))) 32 | (POST "/userdeleted" [name surname] (do (user-list-delete name surname) (layout/render "userdeleted.html" {:name name :surname surname}))) 33 | (GET "/searchuser" [] (layout/render "searchuser.html")) 34 | (POST "/searchresult" [] (layout/render "searchresult.html" {:users @user-list})) 35 | (GET "/about" [] (about-page))) 36 | 37 | -------------------------------------------------------------------------------- /Chapter06/usermanager/test/usermanager/test/handler.clj: -------------------------------------------------------------------------------- 1 | (ns usermanager.test.handler 2 | (:require [clojure.test :refer :all] 3 | [ring.mock.request :refer :all] 4 | [usermanager.handler :refer :all])) 5 | 6 | (deftest test-app 7 | (testing "main route" 8 | (let [response (app (request :get "/"))] 9 | (is (= 200 (:status response))))) 10 | 11 | (testing "not-found route" 12 | (let [response (app (request :get "/invalid"))] 13 | (is (= 404 (:status response)))))) 14 | -------------------------------------------------------------------------------- /Chapter07/ansdocker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM williamyeh/ansible:centos7 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Chapter07/ansdocker/ansible/inventory: -------------------------------------------------------------------------------- 1 | localhost -------------------------------------------------------------------------------- /Chapter07/ansdocker/ansible/playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | vars: 4 | http_port: 80 5 | max_clients: 200 6 | remote_user: root 7 | tasks: 8 | - name: ensure apache is at the latest version 9 | yum: name=httpd state=latest 10 | -------------------------------------------------------------------------------- /Chapter07/salt/srv/salt/state/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - webserver -------------------------------------------------------------------------------- /Chapter07/salt/srv/salt/state/webserver.sls: -------------------------------------------------------------------------------- 1 | apache2: # ID declaration 2 | pkg: # state declaration 3 | - installed # function declaration -------------------------------------------------------------------------------- /Chapter07/salt/srv/salt/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | '*': 3 | - webserver -------------------------------------------------------------------------------- /Chapter07/salt/srv/salt/webserver.sls: -------------------------------------------------------------------------------- 1 | apache2: # ID declaration 2 | pkg: # state declaration 3 | - installed # function declaration -------------------------------------------------------------------------------- /Chapter08/ganglia/docker-compose.yml: -------------------------------------------------------------------------------- 1 | gmetad: 2 | image: wookietreiber/ganglia 3 | ports: 4 | - "30010:80" 5 | gmond: 6 | image: wookietreiber/ganglia 7 | gmond2: 8 | image: wookietreiber/ganglia 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Practical-DevOps-Second-Edition 5 | Practical DevOps Second Edition, published by Packt 6 | This is the code repository for [Practical DevOps - Second Edition](https://www.packtpub.com/virtualization-and-cloud/practical-devops-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781788392570), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the book from start to finish. 7 | ## About the Book 8 | Virtual and ADevOps is a practical field that focuses on delivering business value as efficiently as possible. DevOps encompasses all code workflows from testing environments to production environments. It stresses cooperation between different roles, and how they can work together more closely, as the roots of the word imply—Development and Operations. 9 | ## Instructions and Navigation 10 | All of the code is organized into folders. Each folder starts with a number followed by the application name. For example, Chapter02. 11 | 12 | 13 | 14 | The code will look like the following: 15 | ``` 16 | --- 17 | - hosts: localhost 18 | vars: 19 | http_port: 80 20 | max_clients: 200 21 | remote_user: root 22 | tasks: 23 | - name: ensure apache is at the latest version 24 | yum: name=httpd state=latest 25 | ``` 26 | 27 | This book contains many practical examples. To work through the examples, you need a 28 | machine preferably with a GNU/Linux-based operating system, such as Fedora. 29 | 30 | ## Related Products 31 | * [Mastering Chef the DevOps Way by School of DevOps® [Video]](https://www.packtpub.com/virtualization-and-cloud/mastering-chef-devops-way-school-devops®-video?utm_source=github&utm_medium=repository&utm_campaign=9781789345704) 32 | 33 | * [Mastering Puppet the DevOps way by School of DevOps® [Video]](https://www.packtpub.com/virtualization-and-cloud/mastering-puppet-devops-way-school-devops®-video?utm_source=github&utm_medium=repository&utm_campaign=9781789340921) 34 | 35 | * [Mastering Docker (2017) the DevOps way by School of Devops® [Video]](https://www.packtpub.com/networking-and-servers/python-penetration-testing-essentials-second-edition?utm_source=github&utm_medium=repository&utm_campaign=9781789138962) 36 | 37 | ### Suggestions and Feedback 38 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSe5qwunkGf6PUvzPirPDtuy1Du5Rlzew23UBp2S-P3wB-GcwQ/viewform) if you have any feedback or suggestions. 39 | ### Download a free PDF 40 | 41 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
42 |

https://packt.link/free-ebook/9781788392570

--------------------------------------------------------------------------------