diff options
73 files changed, 2576 insertions, 1451 deletions
diff --git a/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 562e43e797..562e43e797 100644 --- a/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,5 +1,6 @@ # Daniel DeLeo Daniel DeLeo <dan@chef.io> Daniel DeLeo <dan@opscode.com> +Daniel DeLeo <dan@chef.io> danielsdeleo <dan@chef.io> Daniel DeLeo <dan@chef.io> Dan DeLeo <danielsdeleo@mac.com> Daniel DeLeo <dan@chef.io> Dan DeLeo <dan@kallistec.com> Daniel DeLeo <dan@chef.io> danielsdeleo <dan@getchef.com> @@ -62,11 +63,11 @@ Nuo Yan <nuo@opscode.com> Nuo Yan <nuoyan@nuo-yans-macbook-pro.(none)> Nuo Yan <nuo@opscode.com> Nuo Yan <nuoyan@nuo-yans-macbook-pro.local> # Thom May -Thom May <thom@clearairturbulence.org> -Thom May <thom@clearairturbulence.org> Thom May <thom.may@betfair.com> -Thom May <thom@clearairturbulence.org> Thom May <thom@digital-science.com> -Thom May <thom@clearairturbulence.org> Thom May <thom@virelais.nyc.joostas.com> -Thom May <thom@clearairturbulence.org> Thom May <tmay@expedia.com> +Thom May <thom@chef.io> Thom May <thom@clearairturbulence.org> +Thom May <thom@chef.io> Thom May <thom.may@betfair.com> +Thom May <thom@chef.io> Thom May <thom@digital-science.com> +Thom May <thom@chef.io> Thom May <thom@virelais.nyc.joostas.com> +Thom May <thom@chef.io> Thom May <tmay@expedia.com> # Stephen Delano Stephen Delano <stephen@chef.io> Stephen Delano <stephen@opscode.com> @@ -114,4 +115,56 @@ Marc Paradise <marc@chef.io> marc@opscode.com <marc@opscode.com> Tyler Ball <tyleraball@gmail.com> tyler-ball <tyleraball@gmail.com> # Steven Danna -Steven Danna <steve@chef.io> Steven Danna <steve@opscode.com>
\ No newline at end of file +Steven Danna <steve@chef.io> Steven Danna <steve@opscode.com> + +# Salim Alam +Salim Alam <salam@chef.io> chefsalim <salam@chef.io> + +# Isa Farnik +Isa Farnik <isa@chef.io> curiositycasualty <isa@getchef.com> +Isa Farnik <isa@chef.io> curiositycasualty <isa@opscode.com> + +# Paul Mooring +Paul Mooring <paul@chef.io> Paul Mooring <paul@opscode.com> + +# Jeremiah Snapp +Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah@getchef.com> +Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah.snapp@opscode.com> +Jeremiah Snapp <jeremiah@chef.io> Jeremiah Snapp <jeremiah@opscode.com> + +# Mark Myzk +Mark Mzyk <mmzyk@chef.io> Mark Mzyk <mmzyk@opscode.com> +Mark Mzyk <mmzyk@chef.io> mmzyk <mmzyk@opscode.com> +Mark Mzyk <mmzyk@chef.io> Mark Mzyk <markmzyk@Marks-MacBook-Pro.local> + +# Chris Doherty +Chris Doherty <cdoherty@chef.io> Chris Doherty <cdoherty@getchef.com> +Chris Doherty <cdoherty@chef.io> Chris Doherty <cdoherty@ooyala.com> +Chris Doherty <cdoherty@chef.io> Chris Doherty <randomcamel@users.noreply.github.com> +Chris Doherty <cdoherty@chef.io> unknown <cdoherty@chef.io> + +# Christopher Webber +Christopher Webber <cwebber@chef.io> Christopher Webber <cwebber@getchef.com> + +# Tyler Cloke +Tyler Cloke <tyler@chef.io> tylercloke <tyler@opscode.com> +Tyler Cloke <tyler@chef.io> tylercloke <tylercloke@gmail.com> +Tyler Cloke <tyler@chef.io> Tyler Cloke <tylercloke@gmail.com> + +# Julian Dunn +Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@getchef.com> +Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@opscode.com> +Julian C. Dunn <jdunn@chef.io> Julian C. Dunn <jdunn@aquezada.com> + +# Tom Duffield +Tom Duffield <tom@chef.io> Tom Duffield <tom@getchef.com> +Tom Duffield <tom@chef.io> Tom Duffield <tom@opscode.com> + +# Scott Hain +Scott Hain <shain@chef.io> Scott Hain <shain@getchef.com> + +# Peter Burkholder +Peter Burkholder <pburkholder@chef.io> Peter Burkholder <peterb@getchef.com> + +# JJ Ashgar +JJ Asghar <jj@chef.io> JJ Asghar <jj@getchef.com> diff --git a/.travis.yml b/.travis.yml index 2416a064f3..b81c538737 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,109 +110,144 @@ matrix: TEST_GEM: poise script: tasks/bin/run_external_test $TEST_GEM rake spec rvm: 2.2 - # ### START TEST KITCHEN ONLY ### # - rvm: 2.2 + services: docker + sudo: required gemfile: kitchen-tests/Gemfile before_install: - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) - - echo -n $DO_KEY_CHUNK_{0..30} >> ~/.ssh/id_aws.base64 - - cat ~/.ssh/id_aws.base64 | tr -d ' ' | base64 --decode > ~/.ssh/id_aws.pem before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) - cd kitchen-tests script: - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen test ubuntu-1404; fi + - bundle exec kitchen test ubuntu-1204 after_failure: - cat .kitchen/logs/kitchen.log - after_script: - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen destroy ubuntu-1404; fi env: - - UBUNTU=1 + - UBUNTU=12.04 - KITCHEN_YAML=.kitchen.travis.yml - - EC2_SSH_KEY_PATH=~/.ssh/id_aws.pem - - secure: VAauyVnAMWhqvnhJOJ/tCDn3XAdWqzbWiDVQPNBkqtm2SBIvhmZl2hlrusvw6YLU31Prdf8fSFhOSysVQQs/rJYrmD/1BfV79p6M7cGXYZ0nGWwldF81N296lyFoZLyrqtmG4G0cx3Pw2ojADFgFe+B5eTGlqJFD+z371g4RF/Y= - - secure: A+qtUF2LPJGkUAdvt04AwZMt69rzaeTyR0/1XEOAuntBKKXSCzddUzr5ePDc9QQ/57AWywKxhVLpnxk3QzKN7r7zerDxyIJBgklNDpNAKkeQjP3T6FpaKEIN9ROcpPtsM6FJ5Agb+bEQoRJF7s+ampO3wLV3XpTiWNuWkcAhv9A= - - secure: J8JIg15trrPgc8X/1DsaUWDQCdDWTvN/AorXzZ/ReudHS6G/KpoynZ5lTmKjlgFiFNE/TGMDv486pStGtIcarTKTuIEmNADdEWlAVH7bxclpayMjtppVuapRCkZWccs5gz5CJyhX7yhQCFTYoqVox9Y4qHGCluF3oqCcPRtCOOw= - - secure: NJYn0blTMwIoFxZlsoMWK8hPO/fi45rgWOqEImnjvSRk++5WL+GgjLBgLvEi7wCMkBijhIMWtnva60ojd4MrxeS7evrmGRjJKXnPuSKEsrGbArZPskBjCAcg+3PlnQQUkFf6hvbGD3HZlJtcbs4hrx8tbDT2Ie7bmQfqpsawKY4= - - secure: FipoX1VzZkzPUP6Gxd05DEva7cX6xKK2Wdq+Y18nNkyW2afPLXCNl5kCsNrgvbqAzbjKaP2M8+b0zwKjrFzNebqmmx1RRfZUJWUkNRF1EgE+tHytmMZW6tNcQlTlvA0KqXi4Dt6SIQ0l/DhwwNKZ80jmpiyYi/ErxIXzbVgVtYA= - - secure: T2MbE9twIkdaor796/lDioCgb2+FP3G8lXq+lIqnjaL22WMP8yKtkjNo8ggSlvQZE7MAQHqi5LISw5MU2MI6ImTU50/pgdWreM5Cx37WWYqntcbJ0Sz7v396KGJzeqbDql1fGolHDlykfi+OJzzbIGC8cjz7iAD2RUZU95wEC5s= - - secure: hWEQInvuanQavFCE3m6/q9BjNEFZQmLc94EWnBKTMiwUAdYgQQMLohN7K1Gc8irxYKp86F+P+XWE4lfDZNK3sqmxyk51TtT2EfmKWs+jSLq4+NBYQwXCpRELC5Irpm0GRCYthhsQSuarpVWss/0s0o7iJQaHxrSPcQiwDouIpwU= - - secure: OllJUaR/WUu+H0FIjU7vQxU10JT4d+/FZuTqnX6ZTcXN3dXCirnabYp/j+r5OBY3QeOojOyzGfHUWYEUGH/PTxcxYjrohtFTWht9N9x+SxfX2fLqieH/kRKyDmIidsY8qKChf/LD9f+SwpXRXND/PctKhNR4C5BH57fGUEqE9FU= - - secure: KgKnGtM4e+cVYfLn78eTWJ1q4ORv128abB72QBc/xiSh0rvxSIojVKZCXmRetQPXIl7NoIzU2IyjR1ABEZ+vA83PayTEsOr2KDRDgolSIgZSSiDFt4U2phQsxl4fX7wFv/jWlbxM2fysKBSIRAF57CwBjGhLjmpUO+5PdoR7N2s= - - secure: IgOx4STauKnJWENQGcn2iBp32XcNd2anNR0Fua0ugjudu1+CV+IxcIhI8ohOfZEXyVK4MGTF8uXWrYtoiwyExG4mTXqpRWJCgIkncqiWlfT+8BoAGWxCQhUYub3MaNZANPgebKPJhTPQ8OwNz09gPMNkewRfAqNF05eb8FU2kGA= - - secure: CPXP6g3c1FH4Zm4U19XaPvq9nnyNsQCXRkxiPcGqsJZsGG2QMgzPQyjiAuPqnWxxZHit/6NgzUszJC+skSgcTzDTeD6rOA0Wcxtbr/Un4RRxRnTcRc6mSEZqSu9RbAZMYur/mSQ9HDHnjFe1ok85He4s9jM1iFdgjtg1ToelEmA= - - secure: fp9pzNe09PIyZ/8NjbMPGW1zdG3Q/KhJ+stUKqA+FRopAMX/Hh24gFIVJhFOmfr4Vhn0J8sF7RsFaR1mdzcPewliOzKxknWhGEGMcG9LFCZcv+vVK0Fxs4nUzCRtaXUt08FpsRofG0iBvfapZ7YBhK7lslqGVI+fxCd3ZXmayG8= - - secure: NT/6qcecxmuKYOnw1Atc6hsyJlfB6XI2Z1lg7dE0PhlEVW2EpkckHjAc+5hgg8Zt7TifYm2qDQWJwblwPP0mMj3ra4ZIMaZAiG2kzQoZ5kthqwjAV9fatZvrDXi+jd9wBF2hPyiCokAQiTLmKTYjzY2FBqPO3VDLWdf9qZqRmxw= - - secure: MjIWyfquKANh/YeoyHGksdvAUQ4wc2tBCQmq1QcRhKwb7Sy6wcDk1nujDmnGE7HFpZUS6CyoZF7AMzJGFkCzrChpsLQYUP4hc7VjkXOLzi90vJUl+ANq7KPOmxC0MjKpgeHqCysRbTYbUsnJZfbbZbIZjCAjY0YCY2pGniXpvQc= - - secure: AsZLOiFrHkGsY6jp2ShI5kYz78V6PEUyizgtPCWTgevTRGWpdCq9csIEoqUBY+vMUxmQPC6IY4fwHkrRCbv/rJyhwRl/Rnwa3aw8bdD+YD17IxnpXKGXXUyXdTZmF7HzAkVgStehL+qWZ3x9TBdExIV37KVgrVw/b+S0QqBUlQo= - - secure: jwEnSquLreMM1M6N3gGpgTGHd8VtjBUTLDdkrokhiH1jHLpz7Hmr6xeajhZws+2sLtLiB7hYi6WsZBE5VcymBoObh9MeodO9Ve5/1z06lFmx1DyYV6euyo9WUkU2WpoVfu8k7O+eAvyrXXZVqm8Oz1p7Isb6Bh5+fJH2H8rhed4= - - secure: HOAK620U6mlS11XK+JtXTBk26Tt2vWO4shA/6Zit/y0/kAz7JnbXtup7FSysXliBoSv4YsxA6IbgZ8V0tuIXj+q7EcqtHMmQhqzMJG5jRKVhtGiFIhDmwmxJvdfIvwtZOO3mMk0OspLz24sWp8wCciYZMPj0hZJR04R9aWEO3cE= - - secure: DfTRP74UWWxA460XfLoJFgRLwoKbHWNIueL6qr982AnuAxeZFofsxCqxSxcSJmu67TxuPc+b201+BmanHKYmSauGS31t0F4QXk7lCTaT/x38mAPsWvMFkY8HEl56JhmzEp2hAKDB/t0/HItwmvxT1vd5WvNRSSojEVzChftV/zE= - - secure: JoCWsJzTgj+epgzmgbvV7/bdAPHwUGXZA7Jdvv9vIJ5lCo6h9WwCw6/KCvH+bHtrT/RfZmUmxouCxJCLKwts1ZrMmedTIXpMrQJo/YgWRp7ziFnLyZ8jG8bD7rep3ngq1x/cRGc3cZvYN6IK3GS6C27OviYLFsTw74AUnWTaFSo= - - secure: iXfl0WnAnfKurZUrMeV1yOoFiiZ+MKx/Zj6ZVP2++A9EOxxIxb/fS/gIOzSjBQwzrR+fJVHIlX0g42CiBKDQWUvIl5I8kZCVIP6AHa1jyzlmZE9lqSlojz3k5RPS7pW6nIX+z1NHMvtb3e5xeLv8y4J5kwZErqZ+YDJmBRtPxPU= - - secure: RhAW5kABDPB3GWKD+NCg05Kcd92F/+kg+0icXXN166DWQYUut3MLrSY80xNzkz5nXTI9EFU4fUqlKLDiF/kelr0Zp/zpCQAB54o4cu5FkZz0Bgs9k7yUdCRyz6Vt2ChV5cYI4JTn9bMaeXEaGlOjP1iE51rYT6KO6kKlwsEnjUc= - - secure: jy/3fC+UtrDcE/X6/IxkyT2SrYMKkiEMP1ht4d5mxvNA0Xxn43E16c6FNP0JWPpWRGRIP38vnQRB4yOPU9BXvRmmswVL9Ge4e/6flJvKwD5Rlqb2dfaGaHRYV9v8Nkdzl2FvZ9eBH5KHxgG19gCG6L3RXP/+zYwrr4AQdm0fpfw= - - secure: RYEwBWYVXRTEdUWhQxdWXo6tldlVx8pha9zB0rgafcUQxaatAefnRc4X4HXTQnqr2n9TZ2TQGpM8vte/wr6Pjc85VZbimWGzgrvn0kg4MwPR8ZYiEM5qQ/pUpj4+93rpA91PhCGvZoZTqOrXHm4kMPuKro5I6qA4BFUXuANeC/s= - - secure: gHSicpqkqcZT04QurSgszrAiI6HOCw1DBlfIIi9KAJj7mG5GijD/4AQ6HCmcRMbCDJ0nUuvm/kckASnRtF5+3xvIJnuoyyEfCZWxt1lhK2UbS87VU+pVdws/VzwpisXuKsh3H0uT8DDVkWPH/ZWDgfVa74eYDEHiQFjo+2xx5ZA= - - secure: Q42bco3JXEpyVbL2akiOsaCHnAagAFIb3TF6H5qJfaLLqmGs/XrrgxliNaVMfWVSwPT2wpQvg9UGF9x37No9bZBv33DgYcWExmXb/lvGPpkctX37+FTMzECQHxOuUbYPQA7ZEuJ4AA7bwgpMISUeSyz5XXz44KcXIrZK2GWH+X4= - - secure: hugd8NVukJc3redDvlOt6zhaqa63XLNMp/eIIlNllW8VfQ6CJ1P7KJPwgxH24sDyrw7rLzOkBl6R4kaVWsCLCFp+NE6yFFHl9wDkSdLC1OX1DMrJnDsogwUqqe+jX8dxePSy26MSTfG8eo9/NxN9uXr+tKaHoi6G7BRXDHtQ8dQ= - - secure: TRkW9pIuIYHXJmPlDYoddxIp2M2W2f7qBGNJKEMB5xrOezES7w9XTg2eQXrD8jBO+fUUmMnAaDAXZuU58nMysPXx3vhtZKncg8w5CyuXJk2P8nkdPh0u5nmRhEpWrLKtLwJrX48xmJhNQvQqDAyL5c9WUzlWJ4WJFgoP5IDWmLc= - - secure: QHuMdtFCvttiIOx6iS+lH4bKXZMwsgVQ6FPsUW5zJ7uw6mAEWKEil9xNk4aYV9FywinwUs4fnFlnIW/Gj1gLkUjm4DtxdmRZIlRXIbgsNch6H916TCPg4Q2oPsW2nVdXPjW/2jhkfLUiSnuhL+ylami1NF8Up7vokXknh/jFNZU= - - secure: GTfrUVmMQSxho3Ia4Y1ONqKvVMD34GHF2/TJb8UdQV7iH+nVxVXpy3nWaCXa9ri7lRzMefkoVLy0gKK13YoVd7w3d2S3/IfNakC85XfN6VuOzK/FDkA0WoPrgKjcQ64I+3dQ6cgrMWWTieKwRZy+Ve24iRbnN055Hk+VRMu6OGw= - - secure: SOMYGVfHLkHsH6koxpw68YQ4ydEo6YXPhHbrYGQbehUbFa6+OZzBcAJRJbKjyhD2AZRvNr2jB8XnjYKvVyDGQRpkWhGYZ7CpHqINpDsqKBsbiMe3/+KmKQqS+UKxNGefquoOvyQ1N8Xy77dkWYokRtGMEuR12RkZLonxiDW8Qyg= - - secure: bSsDg+dJnPFdFiC/tbb61HdLh/Q0z2RVVAReT1wvV1BN4fN4NydvkUGbQmyFNyyunLulEs+X0oFma9L0497nUlTnan8UOg9sIleTSybPX6E9xSKKCItH1GgDw8bM9Igez5OOrrePBD3altVrH+FmGx0dlTQgM/KZMN50BJ79cXw= - rvm: 2.2 + services: docker + sudo: required gemfile: kitchen-tests/Gemfile before_install: - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) - - echo -n $DO_KEY_CHUNK_{0..30} >> ~/.ssh/id_aws.base64 - - cat ~/.ssh/id_aws.base64 | tr -d ' ' | base64 --decode > ~/.ssh/id_aws.pem before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) - cd kitchen-tests script: - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen test centos-6; fi + - bundle exec kitchen test ubuntu-1404 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - UBUNTU=14.04 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test ubuntu-1604 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - UBUNTU=16.04 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test debian-7 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - DEBIAN=7 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test debian-8 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - DEBIAN=8 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test centos-6 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - CENTOS=6 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test centos-7 + after_failure: + - cat .kitchen/logs/kitchen.log + env: + - CENTOS=7 + - KITCHEN_YAML=.kitchen.travis.yml + - rvm: 2.2 + services: docker + sudo: required + gemfile: kitchen-tests/Gemfile + before_install: + - gem update --system $(grep rubygems omnibus_overrides.rb | cut -d'"' -f2) + - gem install bundler -v $(grep bundler omnibus_overrides.rb | cut -d'"' -f2) + before_script: + - sudo iptables -L DOCKER || ( echo "DOCKER iptables chain missing" ; sudo iptables -N DOCKER ) + - cd kitchen-tests + script: + - bundle exec kitchen test fedora-23 after_failure: - cat .kitchen/logs/kitchen.log - after_script: - - if [ "$TRAVIS_SECURE_ENV_VARS" = "true" ]; then bundle exec kitchen destroy centos-6; fi env: - - CENTOS=1 + - FEDORA=23 - KITCHEN_YAML=.kitchen.travis.yml - - EC2_SSH_KEY_PATH=~/.ssh/id_aws.pem - - secure: VAauyVnAMWhqvnhJOJ/tCDn3XAdWqzbWiDVQPNBkqtm2SBIvhmZl2hlrusvw6YLU31Prdf8fSFhOSysVQQs/rJYrmD/1BfV79p6M7cGXYZ0nGWwldF81N296lyFoZLyrqtmG4G0cx3Pw2ojADFgFe+B5eTGlqJFD+z371g4RF/Y= - - secure: A+qtUF2LPJGkUAdvt04AwZMt69rzaeTyR0/1XEOAuntBKKXSCzddUzr5ePDc9QQ/57AWywKxhVLpnxk3QzKN7r7zerDxyIJBgklNDpNAKkeQjP3T6FpaKEIN9ROcpPtsM6FJ5Agb+bEQoRJF7s+ampO3wLV3XpTiWNuWkcAhv9A= - - secure: J8JIg15trrPgc8X/1DsaUWDQCdDWTvN/AorXzZ/ReudHS6G/KpoynZ5lTmKjlgFiFNE/TGMDv486pStGtIcarTKTuIEmNADdEWlAVH7bxclpayMjtppVuapRCkZWccs5gz5CJyhX7yhQCFTYoqVox9Y4qHGCluF3oqCcPRtCOOw= - - secure: NJYn0blTMwIoFxZlsoMWK8hPO/fi45rgWOqEImnjvSRk++5WL+GgjLBgLvEi7wCMkBijhIMWtnva60ojd4MrxeS7evrmGRjJKXnPuSKEsrGbArZPskBjCAcg+3PlnQQUkFf6hvbGD3HZlJtcbs4hrx8tbDT2Ie7bmQfqpsawKY4= - - secure: FipoX1VzZkzPUP6Gxd05DEva7cX6xKK2Wdq+Y18nNkyW2afPLXCNl5kCsNrgvbqAzbjKaP2M8+b0zwKjrFzNebqmmx1RRfZUJWUkNRF1EgE+tHytmMZW6tNcQlTlvA0KqXi4Dt6SIQ0l/DhwwNKZ80jmpiyYi/ErxIXzbVgVtYA= - - secure: T2MbE9twIkdaor796/lDioCgb2+FP3G8lXq+lIqnjaL22WMP8yKtkjNo8ggSlvQZE7MAQHqi5LISw5MU2MI6ImTU50/pgdWreM5Cx37WWYqntcbJ0Sz7v396KGJzeqbDql1fGolHDlykfi+OJzzbIGC8cjz7iAD2RUZU95wEC5s= - - secure: hWEQInvuanQavFCE3m6/q9BjNEFZQmLc94EWnBKTMiwUAdYgQQMLohN7K1Gc8irxYKp86F+P+XWE4lfDZNK3sqmxyk51TtT2EfmKWs+jSLq4+NBYQwXCpRELC5Irpm0GRCYthhsQSuarpVWss/0s0o7iJQaHxrSPcQiwDouIpwU= - - secure: OllJUaR/WUu+H0FIjU7vQxU10JT4d+/FZuTqnX6ZTcXN3dXCirnabYp/j+r5OBY3QeOojOyzGfHUWYEUGH/PTxcxYjrohtFTWht9N9x+SxfX2fLqieH/kRKyDmIidsY8qKChf/LD9f+SwpXRXND/PctKhNR4C5BH57fGUEqE9FU= - - secure: KgKnGtM4e+cVYfLn78eTWJ1q4ORv128abB72QBc/xiSh0rvxSIojVKZCXmRetQPXIl7NoIzU2IyjR1ABEZ+vA83PayTEsOr2KDRDgolSIgZSSiDFt4U2phQsxl4fX7wFv/jWlbxM2fysKBSIRAF57CwBjGhLjmpUO+5PdoR7N2s= - - secure: IgOx4STauKnJWENQGcn2iBp32XcNd2anNR0Fua0ugjudu1+CV+IxcIhI8ohOfZEXyVK4MGTF8uXWrYtoiwyExG4mTXqpRWJCgIkncqiWlfT+8BoAGWxCQhUYub3MaNZANPgebKPJhTPQ8OwNz09gPMNkewRfAqNF05eb8FU2kGA= - - secure: CPXP6g3c1FH4Zm4U19XaPvq9nnyNsQCXRkxiPcGqsJZsGG2QMgzPQyjiAuPqnWxxZHit/6NgzUszJC+skSgcTzDTeD6rOA0Wcxtbr/Un4RRxRnTcRc6mSEZqSu9RbAZMYur/mSQ9HDHnjFe1ok85He4s9jM1iFdgjtg1ToelEmA= - - secure: fp9pzNe09PIyZ/8NjbMPGW1zdG3Q/KhJ+stUKqA+FRopAMX/Hh24gFIVJhFOmfr4Vhn0J8sF7RsFaR1mdzcPewliOzKxknWhGEGMcG9LFCZcv+vVK0Fxs4nUzCRtaXUt08FpsRofG0iBvfapZ7YBhK7lslqGVI+fxCd3ZXmayG8= - - secure: NT/6qcecxmuKYOnw1Atc6hsyJlfB6XI2Z1lg7dE0PhlEVW2EpkckHjAc+5hgg8Zt7TifYm2qDQWJwblwPP0mMj3ra4ZIMaZAiG2kzQoZ5kthqwjAV9fatZvrDXi+jd9wBF2hPyiCokAQiTLmKTYjzY2FBqPO3VDLWdf9qZqRmxw= - - secure: MjIWyfquKANh/YeoyHGksdvAUQ4wc2tBCQmq1QcRhKwb7Sy6wcDk1nujDmnGE7HFpZUS6CyoZF7AMzJGFkCzrChpsLQYUP4hc7VjkXOLzi90vJUl+ANq7KPOmxC0MjKpgeHqCysRbTYbUsnJZfbbZbIZjCAjY0YCY2pGniXpvQc= - - secure: AsZLOiFrHkGsY6jp2ShI5kYz78V6PEUyizgtPCWTgevTRGWpdCq9csIEoqUBY+vMUxmQPC6IY4fwHkrRCbv/rJyhwRl/Rnwa3aw8bdD+YD17IxnpXKGXXUyXdTZmF7HzAkVgStehL+qWZ3x9TBdExIV37KVgrVw/b+S0QqBUlQo= - - secure: jwEnSquLreMM1M6N3gGpgTGHd8VtjBUTLDdkrokhiH1jHLpz7Hmr6xeajhZws+2sLtLiB7hYi6WsZBE5VcymBoObh9MeodO9Ve5/1z06lFmx1DyYV6euyo9WUkU2WpoVfu8k7O+eAvyrXXZVqm8Oz1p7Isb6Bh5+fJH2H8rhed4= - - secure: HOAK620U6mlS11XK+JtXTBk26Tt2vWO4shA/6Zit/y0/kAz7JnbXtup7FSysXliBoSv4YsxA6IbgZ8V0tuIXj+q7EcqtHMmQhqzMJG5jRKVhtGiFIhDmwmxJvdfIvwtZOO3mMk0OspLz24sWp8wCciYZMPj0hZJR04R9aWEO3cE= - - secure: DfTRP74UWWxA460XfLoJFgRLwoKbHWNIueL6qr982AnuAxeZFofsxCqxSxcSJmu67TxuPc+b201+BmanHKYmSauGS31t0F4QXk7lCTaT/x38mAPsWvMFkY8HEl56JhmzEp2hAKDB/t0/HItwmvxT1vd5WvNRSSojEVzChftV/zE= - - secure: JoCWsJzTgj+epgzmgbvV7/bdAPHwUGXZA7Jdvv9vIJ5lCo6h9WwCw6/KCvH+bHtrT/RfZmUmxouCxJCLKwts1ZrMmedTIXpMrQJo/YgWRp7ziFnLyZ8jG8bD7rep3ngq1x/cRGc3cZvYN6IK3GS6C27OviYLFsTw74AUnWTaFSo= - - secure: iXfl0WnAnfKurZUrMeV1yOoFiiZ+MKx/Zj6ZVP2++A9EOxxIxb/fS/gIOzSjBQwzrR+fJVHIlX0g42CiBKDQWUvIl5I8kZCVIP6AHa1jyzlmZE9lqSlojz3k5RPS7pW6nIX+z1NHMvtb3e5xeLv8y4J5kwZErqZ+YDJmBRtPxPU= - - secure: RhAW5kABDPB3GWKD+NCg05Kcd92F/+kg+0icXXN166DWQYUut3MLrSY80xNzkz5nXTI9EFU4fUqlKLDiF/kelr0Zp/zpCQAB54o4cu5FkZz0Bgs9k7yUdCRyz6Vt2ChV5cYI4JTn9bMaeXEaGlOjP1iE51rYT6KO6kKlwsEnjUc= - - secure: jy/3fC+UtrDcE/X6/IxkyT2SrYMKkiEMP1ht4d5mxvNA0Xxn43E16c6FNP0JWPpWRGRIP38vnQRB4yOPU9BXvRmmswVL9Ge4e/6flJvKwD5Rlqb2dfaGaHRYV9v8Nkdzl2FvZ9eBH5KHxgG19gCG6L3RXP/+zYwrr4AQdm0fpfw= - - secure: RYEwBWYVXRTEdUWhQxdWXo6tldlVx8pha9zB0rgafcUQxaatAefnRc4X4HXTQnqr2n9TZ2TQGpM8vte/wr6Pjc85VZbimWGzgrvn0kg4MwPR8ZYiEM5qQ/pUpj4+93rpA91PhCGvZoZTqOrXHm4kMPuKro5I6qA4BFUXuANeC/s= - - secure: gHSicpqkqcZT04QurSgszrAiI6HOCw1DBlfIIi9KAJj7mG5GijD/4AQ6HCmcRMbCDJ0nUuvm/kckASnRtF5+3xvIJnuoyyEfCZWxt1lhK2UbS87VU+pVdws/VzwpisXuKsh3H0uT8DDVkWPH/ZWDgfVa74eYDEHiQFjo+2xx5ZA= - - secure: Q42bco3JXEpyVbL2akiOsaCHnAagAFIb3TF6H5qJfaLLqmGs/XrrgxliNaVMfWVSwPT2wpQvg9UGF9x37No9bZBv33DgYcWExmXb/lvGPpkctX37+FTMzECQHxOuUbYPQA7ZEuJ4AA7bwgpMISUeSyz5XXz44KcXIrZK2GWH+X4= - - secure: hugd8NVukJc3redDvlOt6zhaqa63XLNMp/eIIlNllW8VfQ6CJ1P7KJPwgxH24sDyrw7rLzOkBl6R4kaVWsCLCFp+NE6yFFHl9wDkSdLC1OX1DMrJnDsogwUqqe+jX8dxePSy26MSTfG8eo9/NxN9uXr+tKaHoi6G7BRXDHtQ8dQ= - - secure: TRkW9pIuIYHXJmPlDYoddxIp2M2W2f7qBGNJKEMB5xrOezES7w9XTg2eQXrD8jBO+fUUmMnAaDAXZuU58nMysPXx3vhtZKncg8w5CyuXJk2P8nkdPh0u5nmRhEpWrLKtLwJrX48xmJhNQvQqDAyL5c9WUzlWJ4WJFgoP5IDWmLc= - - secure: QHuMdtFCvttiIOx6iS+lH4bKXZMwsgVQ6FPsUW5zJ7uw6mAEWKEil9xNk4aYV9FywinwUs4fnFlnIW/Gj1gLkUjm4DtxdmRZIlRXIbgsNch6H916TCPg4Q2oPsW2nVdXPjW/2jhkfLUiSnuhL+ylami1NF8Up7vokXknh/jFNZU= - - secure: GTfrUVmMQSxho3Ia4Y1ONqKvVMD34GHF2/TJb8UdQV7iH+nVxVXpy3nWaCXa9ri7lRzMefkoVLy0gKK13YoVd7w3d2S3/IfNakC85XfN6VuOzK/FDkA0WoPrgKjcQ64I+3dQ6cgrMWWTieKwRZy+Ve24iRbnN055Hk+VRMu6OGw= - - secure: SOMYGVfHLkHsH6koxpw68YQ4ydEo6YXPhHbrYGQbehUbFa6+OZzBcAJRJbKjyhD2AZRvNr2jB8XnjYKvVyDGQRpkWhGYZ7CpHqINpDsqKBsbiMe3/+KmKQqS+UKxNGefquoOvyQ1N8Xy77dkWYokRtGMEuR12RkZLonxiDW8Qyg= - - secure: bSsDg+dJnPFdFiC/tbb61HdLh/Q0z2RVVAReT1wvV1BN4fN4NydvkUGbQmyFNyyunLulEs+X0oFma9L0497nUlTnan8UOg9sIleTSybPX6E9xSKKCItH1GgDw8bM9Igez5OOrrePBD3altVrH+FmGx0dlTQgM/KZMN50BJ79cXw= ### END TEST KITCHEN ONLY ### - rvm: 2.2 sudo: required @@ -246,13 +281,3 @@ notifications: irc: channels: - chat.freenode.net#chef-hacking - webhooks: - urls: - # Gitter IM - - secure: HmMKr/ysKVyKUJ24PRCHcA8QCmlFoukrYumY0GRLzvaFWO8PknHO1t/0RbrKRb2ed/hgkFd+RKNCYvSvcE8Ahr2vlMrBeGHGfVeOGkWtbhLgNqo1b50Ll9CqvTM8X2ZIq6hIWraanwoYRQu/8uGL29yH4lBi7DhpTkFwBMLulhQ= - hipchat: - rooms: - # Build Statuses - - secure: G8MNo94L8bmWWwkH2/ViB2QaZnZHZscYM/mEjDbOGd15sqrruwckeARyBoUcRI7P1C6AFmS4IKCNVXa6KzX4Pbh51gQWM92zRpRTZpplwtXz53/1l8ajLFLLMLvEMTlBFAANUKEUFAQPY4dMa14V3Qc5oijfIncN61k4nZNTKpY= - # Open Source - - secure: hmcex4PpG5dn8WvjndONO4xCUKOC5kPU/bUEGRrfVbe2YKJE7t0XXbNDC96W/xBgzgnJzvf1Er0zJKDrNf4qEDEWFoozdN00WLcqREgaLLS3Seto2FjR/BpBk5q+sCV0rwwEMms2P4Qk+VSnDCnm9EaeM55hOabqNuOrRzoZLBQ= diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b2f9ece975..23dd85d7b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,9 +27,6 @@ Chef is built to last. We strive to ensure high quality throughout the Chef expe continuously on all the pull requests. We require the Travis runs to succeed on every pull request before being merged. -In addition to this it would be nice to include the description of the problem you are solving - with your change. You can use [Chef Issue Template](#issuetemplate) in the description section - of the pull request. ### <a name="cr"></a> Chef Code Review Process @@ -49,15 +46,6 @@ Once you a pull request, the **Chef Engineering Team** or **Chef Core Committers If you would like to learn about when your code will be available in a release of Chef, read more about [Chef Release Process](#release). -### <a name="oh"></a> Developer Office Hours - -We hold regular "office hours" on Google Hangouts On-The-Air that you can join to review contributions together, -ask questions about contributing, or just hang out with Chef Software employees. The regularly scheduled Chef hangouts occur on Mondays and Wednesdays at 3pm Eastern / Noon Pacific. - -The link to join the Hangout or watch it live is usually tweeted from [@ChefOfficeHours](https://twitter.com/ChefOfficeHours) -and posted in the #chef IRC channel on irc.freenode.net when the hangout is about to start. - -You can watch the recordings of the old Code Review hangouts on the [opscodebtm](http://www.youtube.com/opscodebtm) youtube account. ### Contributor License Agreement (CLA) Licensing is very important to open source projects. It helps ensure the @@ -134,29 +122,6 @@ There is also a listing of the various Chef products and where to file issues th Otherwise you can file your issue in the [Chef project](https://github.com/chef/chef/issues) and we will make sure it gets filed against the appropriate project. -In order to decrease the back and forth in issues, and to help us get to the bottom of them quickly - we use the below issue template. You can copy/paste this template into the issue you are opening and - edit it accordingly. - -<a name="issuetemplate"></a> -``` -### Version: -[Version of the project installed] - -### Environment: [Details about the environment such as the Operating System, cookbook details, etc...] - -### Scenario: -[What you are trying to achieve and you can't?] - -### Steps to Reproduce: -[If you are filing an issue what are the things we need to do in order to repro your problem?] - -### Expected Result: -[What are you expecting to happen as the consequence of above reproduction steps?] - -### Actual Result: -[What actually happens after the reproduction steps?] -``` ### Useful Github Queries @@ -190,7 +155,7 @@ We frequently make `alpha` and `beta` releases with version numbers that look li We do a `Minor` release approximately every 3 months and `Patch` releases on a when-needed basis for regressions, significant bugs, and security issues. -Announcements of releases are available on [Chef Blog](http://www.chef.io/blog) when they are +Announcements of releases are available on [Chef Blog](https://www.chef.io/blog/) when they are available. ## Chef Community @@ -198,8 +163,8 @@ Announcements of releases are available on [Chef Blog](http://www.chef.io/blog) Chef is made possible by a strong community of developers and system administrators. If you have any questions or if you would like to get involved in the Chef community you can check out: -* [chef](http://lists.opscode.com/sympa/info/chef) and [chef-dev](http://lists.opscode.com/sympa/info/chef-dev) mailing lists -* [\#chef](https://botbot.me/freenode/chef) and [\#chef-hacking](https://botbot.me/freenode/chef-hacking) IRC channels on irc.freenode.net +* [chef](https://discourse.chef.io/) mailing list +* [\#chef](https://botbot.me/freenode/chef/) and [\#chef-hacking](https://botbot.me/freenode/chef-hacking/) IRC channels on irc.freenode.net Also here are some additional pointers to some awesome Chef content: diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md index 90b57c7e34..069cdc62b4 100644 --- a/DOC_CHANGES.md +++ b/DOC_CHANGES.md @@ -6,17 +6,15 @@ Example Doc Change: Description of the required change. --> -## Doc changes for Chef 12.9 +## Doc changes for Chef 12.11 -### New timeout option added to `knife ssh` +### RFC 062 Exit Status Support -When doing a `knife ssh` call, if a connection to a host is not able -to succeed due to host unreachable or down, the entire call can hang. In -order to prevent this from happening, a new timeout option has been added -to allow a connection timeout to be passed to the underlying SSH call -(see ConnectTimeout setting in http://linux.die.net/man/5/ssh_config) +Starting with Chef Client 12.11, there is support for the consistent, standard exit codes as defined in [Chef RFC 062](https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md). + +With no additional configuration when Chef Client exits with a non-standard exit code a deprecation warning will be issued advising users of the upcoming change in behavior. + +To enable the standardized exit code behavior, there is a new setting in client.rb. The `exit_status` setting, when set to `:enabled` will enforce standarized exit codes. In a future release, this will become the default behavior. + +If you need to maintain the previous exit code behavior to support your current workflow, you can disable this (and the deprecation warnings) by setting `exit_status` to `:disabled`. -The timeout setting can be passed in via a command line parameter -(`-t` or `--ssh-timeout`) or via a knife config -(`Chef::Config[:knife][:ssh_timeout]`). The value of the timeout is set -in seconds. @@ -4,17 +4,12 @@ extend GemfileUtil source "https://rubygems.org" -# Pick the gemspec for our platform -gemspec_name = "chef" -Dir.glob("chef-*.gemspec").each do |gemspec_filename| - gemspec_filename =~ /^chef-(.+).gemspec/ - gemspec_platform = $1 - if Gem::Platform.match(Gem::Platform.new(gemspec_platform)) - Bundler.ui.info "Using gemspec #{gemspec_filename} for current platform." - gemspec_name = "chef-#{gemspec_platform}" - end -end -gemspec name: gemspec_name +# Note we do not use the gemspec DSL which restricts to the +# gemspec for the current platform and filters out other platforms +# during a bundle lock operation. We actually want dependencies from +# both of our gemspecs. Also note this this mimics gemspec behavior +# of bundler versions prior to 1.12.0 (https://github.com/bundler/bundler/commit/193a14fe5e0d56294c7b370a0e59f93b2c216eed) +gem "chef", path: "." gem "chef-config", path: File.expand_path("../chef-config", __FILE__) if File.exist?(File.expand_path("../chef-config", __FILE__)) # Ensure that we can always install rake, regardless of gem groups diff --git a/Gemfile.lock b/Gemfile.lock index 5d7a4cb396..3e3f0d6083 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,9 +18,9 @@ GIT PATH remote: . specs: - chef (12.10.58) + chef (12.11.12) bundler (>= 1.10) - chef-config (= 12.10.58) + chef-config (= 12.11.12) chef-zero (~> 4.5) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -45,9 +45,9 @@ PATH specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (12.10.58-universal-mingw32) + chef (12.11.12-universal-mingw32) bundler (>= 1.10) - chef-config (= 12.10.58) + chef-config (= 12.11.12) chef-zero (~> 4.5) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -87,7 +87,7 @@ PATH PATH remote: chef-config specs: - chef-config (12.10.58) + chef-config (12.11.12) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) @@ -100,19 +100,19 @@ GEM mixlib-cli (~> 1.4) artifactory (2.3.2) ast (2.2.0) - aws-sdk (2.3.5) - aws-sdk-resources (= 2.3.5) - aws-sdk-core (2.3.5) + aws-sdk (2.3.9) + aws-sdk-resources (= 2.3.9) + aws-sdk-core (2.3.9) jmespath (~> 1.0) - aws-sdk-resources (2.3.5) - aws-sdk-core (= 2.3.5) + aws-sdk-resources (2.3.9) + aws-sdk-core (= 2.3.9) aws-sdk-v1 (1.66.0) json (~> 1.4) nokogiri (>= 1.4.4) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) - byebug (9.0.3) + byebug (9.0.5) chef-api (0.6.0) logify (~> 0.1) mime-types @@ -147,7 +147,7 @@ GEM rspec (~> 3.0) coderay (1.1.1) colorize (0.7.7) - compat_resource (12.9.1) + compat_resource (12.10.4) cucumber-core (1.4.0) gherkin (~> 3.2.0) debug_inspector (0.0.2) @@ -158,7 +158,7 @@ GEM erubis (2.7.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - fauxhai (3.4.0) + fauxhai (3.5.0) net-ssh ffi (1.9.10) ffi (1.9.10-x86-mingw32) @@ -174,12 +174,11 @@ GEM yajl-ruby (~> 1.1) fuzzyurl (0.8.0) gherkin (3.2.0) - github_api (0.13.1) + github_api (0.14.0) addressable (~> 2.4.0) descendants_tracker (~> 0.0.4) faraday (~> 0.8, < 0.10) hashie (>= 3.4) - multi_json (>= 1.7.5, < 2.0) oauth2 github_changelog_generator (1.12.1) colorize (~> 0.7) @@ -214,9 +213,9 @@ GEM multi_json (~> 1.10) logify (0.2.0) method_source (0.8.2) - mime-types (3.0) + mime-types (3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0221) + mime-types-data (3.2016.0521) mini_portile2 (2.0.0) mixlib-authentication (1.4.0) mixlib-log @@ -225,7 +224,7 @@ GEM rspec-mocks (~> 3.2) mixlib-cli (1.6.0) mixlib-config (2.2.1) - mixlib-install (1.0.11) + mixlib-install (1.0.12) artifactory mixlib-shellout mixlib-versioning @@ -235,7 +234,7 @@ GEM win32-process (~> 0.8.2) wmi-lite (~> 1.0) mixlib-versioning (1.1.0) - multi_json (1.12.0) + multi_json (1.12.1) multi_xml (0.5.5) multipart-post (2.0.0) net-scp (1.2.1) @@ -334,7 +333,7 @@ GEM sawyer (0.7.0) addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) - serverspec (2.35.0) + serverspec (2.36.0) multi_json rspec (~> 3.0) rspec-its @@ -346,7 +345,7 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.0) slop (3.6.0) - specinfra (2.57.2) + specinfra (2.57.5) net-scp net-ssh (>= 2.7, < 4.0) net-telnet @@ -1,7 +1,7 @@ Chef NOTICE =========== -Developed at Opscode (http://www.opscode.com). +Developed at Chef (https://www.chef.io). Contributors and Copyright holders: @@ -160,6 +160,16 @@ Whenever a change is checked in to `master`, the patch version of `chef` is bump 2. Runs `rake bundle:install` to update the `Gemfile.lock` to include the new version. 3. Pushes to `master` and submits a new build to Chef's Jenkins cluster. +## Bumping the minor version of Chef + +After each "official" stable release we need to bump the minor version. To do this: + +1. Manually increment the minor version in the VERSION file that is in the root of this repo. and reset the patch version to 0. Assuming the current version is `12.10.57` you would edit `VERSION` to be `12.11.0`. +2. Run `bundle exec rake version` which will copy the version to the respective `version.rb` files in chef and chef-config. +3. Run `bundle exec rake bundle:install` to update the base Gemfile.lock + +Submit a PR with the changes made by the above. + ## Component Versions Chef has two sorts of component: ruby components like `berkshelf` and `test-kitchen`, and binary components like `openssl` and even `ruby` itself. diff --git a/ROADMAP.md b/ROADMAP.md index 3db4932fa9..eeec513a3f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4,10 +4,6 @@ This file provides direction for the project as set forth by [RFC030#Roadmap](ht It is drafted by the Project Lead and the Lieutenants, with input from Maintainers and Contributors. -## 2015 Q4 -* Windows - core package provider - - Moving the remaining package providers from the Windows cookbook into core - ## 2016 Q1 * Windows - core windows_feature - Move windows_feature from Windows cookbook, add caching @@ -1 +1 @@ -12.10.59
\ No newline at end of file +12.11.12
\ No newline at end of file diff --git a/acceptance/vendor/bundle/bundler/gems/chef-acceptance-47e931cec100 b/acceptance/vendor/bundle/bundler/gems/chef-acceptance-47e931cec100 new file mode 160000 +Subproject 47e931cec100dce8efae4369a5b03443eaf2b73 diff --git a/acceptance/vendor/bundle/bundler/gems/chef-acceptance-e92ddae46d21 b/acceptance/vendor/bundle/bundler/gems/chef-acceptance-e92ddae46d21 new file mode 160000 +Subproject e92ddae46d2126864698b9c8e4fc4ec2dcc46c5 diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index 2538466b02..2e15e13644 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.10.59" + VERSION = "12.11.12" end # diff --git a/kitchen-tests/.kitchen.travis.yml b/kitchen-tests/.kitchen.travis.yml index 682a1a5f68..8bc41adb00 100644 --- a/kitchen-tests/.kitchen.travis.yml +++ b/kitchen-tests/.kitchen.travis.yml @@ -1,14 +1,16 @@ --- driver: - name: ec2 - aws_ssh_key_id: <%= ENV['AWS_KEYPAIR_NAME'] %> - region: "us-west-2" - availability_zone: "us-west-2a" - security_group_ids: ["travis-ci"] - instance_type: "m3.medium" + name: dokken + privileged: true + chef_version: latest + +transport: + name: dokken provisioner: name: chef_github + root_path: /opt/kitchen + chef_version: latest chef_omnibus_url: "https://omnitruck.chef.io/install.sh" chef_omnibus_install_options: "-c current" github_owner: "chef" @@ -20,22 +22,96 @@ provisioner: client_rb: diff_disabled: true -transport: - ssh_key: <%= ENV['EC2_SSH_KEY_PATH'] %> +verifier: + name: inspec platforms: - - name: ubuntu-14.04 - driver: - # http://cloud-images.ubuntu.com/locator/ec2/ - # 14.04 amd64 us-west-2 hvm:ebs-ssd - image_id: ami-63ac5803 - - name: centos-6 - driver: - image_id: ami-05cf2265 +- name: debian-7 + driver: + image: debian:7 + pid_one_command: /sbin/init + intermediate_instructions: + - RUN /usr/bin/apt-get update + - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools + - RUN /bin/mkdir /var/run/sshd + +- name: debian-8 + driver: + image: debian:8 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update + - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools + +- name: centos-5 + driver: + image: centos:5 + platform: rhel + run_command: /sbin/init + intermediate_instructions: + - RUN yum clean all + - RUN yum install -y which initscripts net-tools sudo + - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers + +- name: centos-6 + driver: + image: centos:6 + run_command: /sbin/init + intermediate_instructions: + - RUN yum clean all + - RUN yum -y install which initscripts net-tools sudo + - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers + +- name: centos-7 + driver: + image: centos:7 + pid_one_command: /usr/lib/systemd/systemd + intermediate_instructions: + - RUN yum clean all + - RUN yum -y install which initscripts net-tools sudo + - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers + +- name: fedora-23 + driver: + image: fedora:23 + pid_one_command: /usr/lib/systemd/systemd + intermediate_instructions: + - RUN dnf -y install yum which initscripts net-tools sudo + - RUN sed -i -e "s/Defaults.*requiretty.*/Defaults !requiretty/g" /etc/sudoers + +- name: ubuntu-12.04 + driver: + image: ubuntu-upstart:12.04 + pid_one_command: /sbin/init + intermediate_instructions: + - RUN /usr/bin/apt-get update + - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools + +- name: ubuntu-14.04 + driver: + image: ubuntu-upstart:14.04 + pid_one_command: /sbin/init + intermediate_instructions: + - RUN /usr/bin/apt-get update + - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools + +- name: ubuntu-16.04 + driver: + image: ubuntu:16.04 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN /usr/bin/apt-get update + - RUN /usr/bin/apt-get -y install zlib1g-dev sudo net-tools + +- name: opensuse-13.2 + driver: + image: opensuse:13.2 + pid_one_command: /bin/systemd + intermediate_instructions: + - RUN zypper refresh suites: - name: webapp run_list: - recipe[base::default] - - recipe[webapp::default] - attributes: +# - recipe[webapp::default] diff --git a/kitchen-tests/.kitchen.yml b/kitchen-tests/.kitchen.yml index af7ac3cde5..c02ea55a02 100644 --- a/kitchen-tests/.kitchen.yml +++ b/kitchen-tests/.kitchen.yml @@ -5,6 +5,9 @@ driver: cpus: 4 memory: 2048 +verifier: + name: inspec + provisioner: name: chef_github chef_omnibus_url: "https://omnitruck.chef.io/install.sh" @@ -19,17 +22,15 @@ provisioner: platforms: - name: ubuntu-12.04 - name: ubuntu-14.04 - # needs updates for 16.04 - #- name: ubuntu-16.04 - # needs updates for 7.2 - #- name: centos-7.2 + - name: ubuntu-16.04 + - name: centos-7.2 - name: centos-6.7 # needs fixing for 5.11 - #- name: centos-5.11 + # - name: centos-5.11 suites: - name: webapp run_list: - recipe[base::default] - - recipe[webapp::default] +# - recipe[webapp::default] attributes: diff --git a/kitchen-tests/Berksfile b/kitchen-tests/Berksfile index 23c72d5394..31e49b3e18 100644 --- a/kitchen-tests/Berksfile +++ b/kitchen-tests/Berksfile @@ -1,6 +1,8 @@ source "https://supermarket.getchef.com" -cookbook "webapp", :path => "cookbooks/webapp" -cookbook "base", :path => "cookbooks/base" +cookbook "webapp", path: "cookbooks/webapp" +cookbook "base", path: "cookbooks/base" cookbook "php", "~> 1.5.0" + +cookbook "resolver", github: "chef-cookbooks/resolver", branch: "lcg/docker" diff --git a/kitchen-tests/Berksfile.lock b/kitchen-tests/Berksfile.lock index b5fa7aba13..f4a9de89e2 100644 --- a/kitchen-tests/Berksfile.lock +++ b/kitchen-tests/Berksfile.lock @@ -2,6 +2,10 @@ DEPENDENCIES base path: cookbooks/base php (~> 1.5.0) + resolver + git: git://github.com/chef-cookbooks/resolver.git + revision: dd65ab8e2346cc0739c13682c74868f5b939b06a + branch: lcg/docker webapp path: cookbooks/webapp @@ -14,7 +18,7 @@ GRAPH apt (>= 0.0.0) build-essential (>= 0.0.0) chef-client (>= 0.0.0) - fail2ban (>= 0.0.0) + chef_hostname (>= 0.0.0) logrotate (>= 0.0.0) multipackage (>= 0.0.0) nscd (>= 0.0.0) @@ -26,15 +30,18 @@ GRAPH ubuntu (>= 0.0.0) users (>= 0.0.0) yum-epel (>= 0.0.0) - build-essential (3.2.0) + build-essential (4.0.0) + mingw (>= 0.0.0) seven_zip (>= 0.0.0) - chef-client (4.5.0) + chef-client (4.5.2) cron (>= 1.7.0) logrotate (>= 1.9.0) windows (>= 1.39.0) chef-sugar (3.3.0) - chef_handler (1.3.0) - compat_resource (12.9.1) + chef_handler (1.4.0) + chef_hostname (0.4.1) + compat_resource (>= 0.0.0) + compat_resource (12.10.4) cron (1.7.6) database (2.3.1) aws (>= 0.0.0) @@ -42,12 +49,13 @@ GRAPH mysql-chef_gem (~> 0.0) postgresql (>= 1.0.0) xfs (>= 0.0.0) - fail2ban (2.3.0) - yum-epel (>= 0.0.0) iis (4.1.7) windows (>= 1.34.6) iptables (2.2.0) logrotate (1.9.2) + mingw (1.0.0) + compat_resource (>= 0.0.0) + seven_zip (>= 0.0.0) multipackage (3.0.28) compat_resource (>= 0.0.0) mysql (5.6.3) @@ -55,11 +63,12 @@ GRAPH mysql-chef_gem (0.0.5) build-essential (>= 0.0.0) mysql (>= 0.0.0) - nscd (2.0.0) + nscd (4.0.0) compat_resource (>= 0.0.0) - ntp (1.11.0) + ntp (2.0.0) windows (>= 1.38.0) - ohai (3.0.1) + ohai (4.0.2) + compat_resource (>= 12.9.0) openssh (2.0.0) iptables (>= 1.0) openssl (4.4.0) @@ -75,9 +84,9 @@ GRAPH apt (>= 1.9.0) build-essential (>= 0.0.0) openssl (~> 4.0) - resolver (1.3.0) + resolver (1.3.1) selinux (0.9.0) - seven_zip (2.0.0) + seven_zip (2.0.1) windows (>= 1.2.2) sudo (2.9.0) ubuntu (1.2.0) @@ -88,7 +97,7 @@ GRAPH database (~> 2.3.1) mysql (~> 5.6.3) php (~> 1.5.0) - windows (1.40.0) + windows (1.41.0) chef_handler (>= 0.0.0) xfs (2.0.1) xml (2.0.0) diff --git a/kitchen-tests/Gemfile b/kitchen-tests/Gemfile index acc62156ae..ad89269a75 100644 --- a/kitchen-tests/Gemfile +++ b/kitchen-tests/Gemfile @@ -1,10 +1,11 @@ source "https://rubygems.org" -group :end_to_end do - gem "berkshelf" - gem "test-kitchen", "~> 1.4" - gem "kitchen-appbundle-updater" - gem "kitchen-vagrant", "~> 0.17" - gem "kitchen-ec2", github: "test-kitchen/kitchen-ec2" - gem "vagrant-wrapper" -end +gem "berkshelf" +gem "kitchen-appbundle-updater" +gem "kitchen-dokken" +gem "kitchen-ec2" +gem "kitchen-inspec" +gem "kitchen-vagrant" +gem "ridley" +gem "test-kitchen" +gem "vagrant-wrapper" diff --git a/kitchen-tests/Gemfile.lock b/kitchen-tests/Gemfile.lock index 69c23ad156..3f59bf90fe 100644 --- a/kitchen-tests/Gemfile.lock +++ b/kitchen-tests/Gemfile.lock @@ -1,26 +1,15 @@ -GIT - remote: git://github.com/test-kitchen/kitchen-ec2.git - revision: fec3f199a646980dc289ac6db9f90e9a9e4b0f6b - specs: - kitchen-ec2 (1.0.0) - aws-sdk (~> 2) - excon - multi_json - retryable (~> 2.0) - test-kitchen (~> 1.4, >= 1.4.1) - GEM remote: https://rubygems.org/ specs: addressable (2.4.0) artifactory (2.3.2) - aws-sdk (2.3.2) - aws-sdk-resources (= 2.3.2) - aws-sdk-core (2.3.2) + aws-sdk (2.3.8) + aws-sdk-resources (= 2.3.8) + aws-sdk-core (2.3.8) jmespath (~> 1.0) - aws-sdk-resources (2.3.2) - aws-sdk-core (= 2.3.2) - berkshelf (4.3.2) + aws-sdk-resources (2.3.8) + aws-sdk-core (= 2.3.8) + berkshelf (4.3.3) addressable (~> 2.3, >= 2.3.4) berkshelf-api-client (~> 2.0, >= 2.0.2) buff-config (~> 1.0) @@ -49,34 +38,70 @@ GEM buff-ruby_engine (0.1.0) buff-shell_out (0.2.0) buff-ruby_engine (~> 0.1.0) + builder (3.2.2) celluloid (0.16.0) timers (~> 4.0.0) celluloid-io (0.16.2) celluloid (>= 0.16.0) nio4r (>= 1.1.0) - chef-config (12.9.41) + chef-config (12.10.24) fuzzyurl (~> 0.8.0) mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) cleanroom (1.0.0) + coderay (1.1.1) diff-lcs (1.2.5) + docker-api (1.26.2) + excon (>= 0.38.0) + json erubis (2.7.0) excon (0.49.0) faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.10-x86-mingw32) + ffi (1.9.10) fuzzyurl (0.8.0) + gssapi (1.2.0) + ffi (>= 1.0.1) + gyoku (1.3.1) + builder (>= 2.1.2) hashie (3.4.4) hitimes (1.2.4) - hitimes (1.2.4-x86-mingw32) httpclient (2.7.2) + inspec (0.22.1) + hashie (~> 3.4) + json (~> 1.8) + method_source (~> 0.8) + pry (~> 0) + r-train (~> 0.12) + rainbow (~> 2) + rspec (~> 3) + rspec-its (~> 1.2) + rubyzip (~> 1.1) + thor (~> 0.19) jmespath (1.2.4) json_pure (>= 1.8.1) json (1.8.3) json_pure (1.8.3) kitchen-appbundle-updater (0.1.2) + kitchen-dokken (0.0.29) + docker-api (~> 1.26.2) + test-kitchen (~> 1.5) + kitchen-ec2 (1.0.0) + aws-sdk (~> 2) + excon + multi_json + retryable (~> 2.0) + test-kitchen (~> 1.4, >= 1.4.1) + kitchen-inspec (0.13.0) + inspec (>= 0.20.0, < 1.0.0) + test-kitchen (~> 1.6) kitchen-vagrant (0.20.0) test-kitchen (~> 1.4) + little-plugger (1.1.4) + logging (2.1.0) + little-plugger (~> 1.1) + multi_json (~> 1.10) + method_source (0.8.2) minitar (0.5.4) mixlib-authentication (1.4.0) mixlib-log @@ -90,21 +115,32 @@ GEM mixlib-versioning mixlib-log (1.6.0) mixlib-shellout (2.2.6) - mixlib-shellout (2.2.6-universal-mingw32) - win32-process (~> 0.8.2) - wmi-lite (~> 1.0) mixlib-versioning (1.1.0) molinillo (0.4.5) - multi_json (1.12.0) + multi_json (1.12.1) multipart-post (2.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (3.1.1) nio4r (1.2.1) + nori (2.6.0) octokit (4.3.0) sawyer (~> 0.7.0, >= 0.5.3) + pry (0.10.3) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + r-train (0.12.1) + docker-api (~> 1.26) + json (~> 1.8) + mixlib-shellout (~> 2.0) + net-scp (~> 1.2) + net-ssh (>= 2.9, < 4.0) + winrm (~> 1.6) + winrm-fs (~> 0.3) + rainbow (2.1.0) retryable (2.0.3) - ridley (4.5.0) + ridley (4.5.1) addressable buff-config (~> 1.0) buff-extensions (~> 1.0) @@ -122,20 +158,30 @@ GEM retryable (~> 2.0) semverse (~> 1.1) varia_model (~> 0.4.0) + rspec (3.4.0) + rspec-core (~> 3.4.0) + rspec-expectations (~> 3.4.0) + rspec-mocks (~> 3.4.0) rspec-core (3.4.4) rspec-support (~> 3.4.0) rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) + rspec-its (1.2.0) + rspec-core (>= 3.0.0) + rspec-expectations (>= 3.0.0) rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-support (3.4.1) + rubyntlm (0.6.0) + rubyzip (1.2.0) safe_yaml (1.0.4) sawyer (0.7.0) addressable (>= 2.3.5, < 2.5) faraday (~> 0.8, < 0.10) semverse (1.2.1) + slop (3.6.0) solve (2.0.3) molinillo (~> 0.4.2) semverse (~> 1.1) @@ -153,20 +199,32 @@ GEM varia_model (0.4.1) buff-extensions (~> 1.0) hashie (>= 2.0.2, < 4.0.0) - win32-process (0.8.3) - ffi (>= 1.0.0) - wmi-lite (1.0.0) + winrm (1.8.1) + builder (>= 2.1.2) + gssapi (~> 1.2) + gyoku (~> 1.0) + httpclient (~> 2.2, >= 2.2.0.2) + logging (>= 1.6.1, < 3.0) + nori (~> 2.0) + rubyntlm (~> 0.6.0) + winrm-fs (0.4.2) + erubis (~> 2.7) + logging (>= 1.6.1, < 3.0) + rubyzip (~> 1.1) + winrm (~> 1.5) PLATFORMS ruby - x86-mingw32 DEPENDENCIES berkshelf kitchen-appbundle-updater - kitchen-ec2! - kitchen-vagrant (~> 0.17) - test-kitchen (~> 1.4) + kitchen-dokken + kitchen-ec2 + kitchen-inspec + kitchen-vagrant + ridley + test-kitchen vagrant-wrapper BUNDLED WITH diff --git a/kitchen-tests/cookbooks/base/attributes/default.rb b/kitchen-tests/cookbooks/base/attributes/default.rb index d4e5d1ee5a..ef273c969c 100644 --- a/kitchen-tests/cookbooks/base/attributes/default.rb +++ b/kitchen-tests/cookbooks/base/attributes/default.rb @@ -1,9 +1,10 @@ +puts "CHEF SUGAR THINKS WE ARE ON UBUNTU" if ubuntu? +puts "CHEF SUGAR THINKS WE ARE ON RHEL" if rhel? + # # ubuntu cookbook overrides # -default["ubuntu"]["archive_url"] = "mirror://mirrors.ubuntu.com/mirrors.txt" -default["ubuntu"]["security_url"] = "mirror://mirrors.ubuntu.com/mirrors.txt" default["ubuntu"]["include_source_packages"] = true default["ubuntu"]["components"] = "main restricted universe multiverse" @@ -78,3 +79,9 @@ default["resolver"]["search"] = "chef.io" default["authorization"]["sudo"]["passwordless"] = true default["authorization"]["sudo"]["users"] = %w{vagrant centos ubuntu} + +# +# nscd cookbook overrides +# + +default["nscd"]["server_user"] = "nobody" diff --git a/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb b/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb new file mode 100644 index 0000000000..90d02a361f --- /dev/null +++ b/kitchen-tests/cookbooks/base/libraries/chef-sugar.rb @@ -0,0 +1,4 @@ +require "chef/sugar" + +# hack until this gets baked into chef-sugar so we can use chef-sugar in attributes files +Chef::Node.send(:include, Chef::Sugar::DSL) diff --git a/kitchen-tests/cookbooks/base/metadata.rb b/kitchen-tests/cookbooks/base/metadata.rb index 9e5e792f89..3811fe914d 100644 --- a/kitchen-tests/cookbooks/base/metadata.rb +++ b/kitchen-tests/cookbooks/base/metadata.rb @@ -6,10 +6,12 @@ description "Installs/Configures base" long_description "Installs/Configures base" version "0.1.0" +gem "chef-sugar" + depends "apt" depends "build-essential" depends "chef-client" -depends "fail2ban" +depends "chef_hostname" depends "logrotate" depends "multipackage" depends "nscd" diff --git a/kitchen-tests/cookbooks/base/recipes/default.rb b/kitchen-tests/cookbooks/base/recipes/default.rb index 4ddd7a7b04..053a689b27 100644 --- a/kitchen-tests/cookbooks/base/recipes/default.rb +++ b/kitchen-tests/cookbooks/base/recipes/default.rb @@ -5,13 +5,18 @@ # Copyright (C) 2014 # -if node[:platform_family] == "debian" - include_recipe "apt" +hostname "chef-travis-ci.chef.io" + +if node["platform_family"] == "debian" include_recipe "ubuntu" + apt_update "packages" end -if %w{rhel fedora}.include?(node[:platform_family]) +if %w{rhel fedora}.include?(node["platform_family"]) include_recipe "selinux::disabled" +end + +if node["platform_family"] == "rhel" include_recipe "yum-epel" end @@ -31,9 +36,10 @@ include_recipe "chef-client::delete_validation" include_recipe "chef-client::config" include_recipe "chef-client" -include_recipe "openssh" +# hack needed for debian-7 on docker +directory "/var/run/sshd" -include_recipe "fail2ban" +include_recipe "openssh" include_recipe "nscd" diff --git a/kitchen-tests/cookbooks/base/recipes/packages.rb b/kitchen-tests/cookbooks/base/recipes/packages.rb index f242951a4c..c3a552b05c 100644 --- a/kitchen-tests/cookbooks/base/recipes/packages.rb +++ b/kitchen-tests/cookbooks/base/recipes/packages.rb @@ -1,6 +1,6 @@ -pkgs = %w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversion git traceroute htop iptraf tmux s3cmd sysbench } +pkgs = %w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversion git traceroute htop tmux s3cmd sysbench } # this deliberately calls the multipackage API N times in order to do one package installation in order to exercise the # multipackage cookbook. diff --git a/kitchen-tests/test/integration/webapp/default_spec.rb b/kitchen-tests/test/integration/webapp/default_spec.rb new file mode 100644 index 0000000000..ec23a57998 --- /dev/null +++ b/kitchen-tests/test/integration/webapp/default_spec.rb @@ -0,0 +1,118 @@ +#describe port(80) do +# it { should be_listening } +# its('processes') {should include 'http'} +#end +# +#describe command("curl http://localhost/index.html") do +# its("stdout") { should match /Hello, World!/ } +#end + +case os[:family] +when "debian", "ubuntu" + ssh_package = "openssh-client" + ssh_service = "ssh" + ntp_service = "ntp" +when "centos", "redhat", "fedora" + ssh_package = "openssh-clients" + ssh_service = "sshd" + ntp_service = "ntpd" +else + raise "i don't know the family #{os[:family]}" +end + +describe package("nscd") do + it { should be_installed } +end + +describe service("nscd") do + # broken? + # it { should be_enabled } + it { should be_installed } + it { should be_running } +end + +describe package(ssh_package) do + it { should be_installed } +end + +describe service(ssh_service) do + it { should be_enabled } + it { should be_installed } + it { should be_running } +end + +describe sshd_config do + its("Protocol") { should cmp 2 } + its("GssapiAuthentication") { should cmp "no" } + its("UseDns") { should cmp "no" } +end + +describe ssh_config do + its("StrictHostKeyChecking") { should cmp "no" } + its("GssapiAuthentication") { should cmp "no" } +end + +describe package("ntp") do + it { should be_installed } +end + +describe service(ntp_service) do + # broken? + # it { should be_enabled } + it { should be_installed } + it { should be_running } +end + +describe service("chef-client") do + it { should be_enabled } + it { should be_installed } + it { should be_running } +end + +describe file("/etc/resolv.conf") do + its("content") { should match /search\s+chef.io/ } + its("content") { should match /nameserver\s+8.8.8.8/ } + its("content") { should match /nameserver\s+8.8.4.4/ } +end + +describe package("gcc") do + it { should be_installed } +end + +describe package("flex") do + it { should be_installed } +end + +describe package("bison") do + it { should be_installed } +end + +describe package("autoconf") do + it { should be_installed } +end + +%w{lsof tcpdump strace zsh dmidecode ltrace bc curl wget telnet subversion git traceroute htop tmux s3cmd sysbench }.each do |pkg| + describe package pkg do + it { should be_installed } + end +end + +describe etc_group.where(group_name: "sysadmin") do + its("users") { should include "adam" } + its("gids") { should eq [2300] } +end + +describe passwd.users("adam") do + its("uids") { should eq ["666"] } +end + +describe ntp_conf do + its("server") { should_not eq nil } +end + +# busted inside of docker containers? +describe port(22) do + it { should be_listening } + its("protocols") { should include "tcp" } + its("processes") { should eq ["sshd"] } +end diff --git a/kitchen-tests/test/integration/webapp/serverspec/Gemfile b/kitchen-tests/test/integration/webapp/serverspec/Gemfile deleted file mode 100644 index eef1450f7a..0000000000 --- a/kitchen-tests/test/integration/webapp/serverspec/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -# This Gemfile is only needed so that busser will install gems it needs for serverspec_helper.rb to work -source "https://rubygems.org" - -gem "ffi-yajl", "~> 1.1" # Go away, JSON gem diff --git a/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock b/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock deleted file mode 100644 index ac6c11f28c..0000000000 --- a/kitchen-tests/test/integration/webapp/serverspec/Gemfile.lock +++ /dev/null @@ -1,19 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - ffi (1.9.10) - ffi (1.9.10-x86-mingw32) - ffi-yajl (1.4.0) - ffi (~> 1.5) - libyajl2 (~> 1.2) - libyajl2 (1.2.0) - -PLATFORMS - ruby - x86-mingw32 - -DEPENDENCIES - ffi-yajl (~> 1.1) - -BUNDLED WITH - 1.11.2 diff --git a/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb b/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb deleted file mode 100644 index 992e4f7683..0000000000 --- a/kitchen-tests/test/integration/webapp/serverspec/localhost/default_spec.rb +++ /dev/null @@ -1,127 +0,0 @@ - -require "net/http" -require "uri" - -require "#{ENV['BUSSER_ROOT']}/../kitchen/data/serverspec_helper" - -describe "webapp::default", :end_to_end => true do - - describe "installed packages" do - shared_examples_for "a package" do - it "is installed" do - expect(package(package_name)).to be_installed - end - end - - describe "#{property[:apache][:package]} package" do - include_examples "a package" do - let(:package_name) { property[:apache][:package] } - end - end - - describe "#{property[:mysql][:server_package]} package" do - include_examples "a package" do - let(:package_name) { property[:mysql][:server_package] } - end - end - - describe "#{property[:mysql][:client_package]} package" do - include_examples "a package" do - let(:package_name) { property[:mysql][:client_package] } - end - end - - describe "php package" do - include_examples "a package" do - let(:package_name) { property[:php][:package] } - end - end - end - - describe "enabled/running services" do - shared_examples_for "a service" do - it "is enabled" do - expect(service(service_name)).to be_enabled - end - - it "is running" do - expect(service(service_name)).to be_enabled - end - end - - describe "#{property[:apache][:service_name]} service" do - include_examples "a service" do - let(:service_name) { property[:apache][:service_name] } - end - end - - describe "mysql service" do - include_examples "a service" do - let(:service_name) { property[:mysql][:service_name] } - end - end - - end - - describe "mysql database" do - let(:db_query) { "mysql -u root -pilikerandompasswordstoo -e \"#{statement}\"" } - let(:statement) { "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='webapp'" } - it "creates a database called 'webapp'" do - expect(command(db_query).stdout).to match /webapp/ - end - - describe "mysql database user 'webapp'" do - let(:statement) { "SELECT Host, Db FROM mysql.db WHERE User='webapp'\\G" } - it "adds user 'webapp' to database 'webapp@localhost'" do - expect(command(db_query).stdout).to match /Host: localhost\n Db: webapp/ - end - - describe "grants" do - shared_examples_for "a privilege" do |priv| - let(:statement) { - "SELECT #{priv_query}" \ - " FROM mysql.db" \ - " WHERE Host='localhost' AND Db='webapp' AND User='webapp'\\G" - } - let(:priv_query) { "#{priv.capitalize}_priv" } - - it "has privilege #{priv} on 'webapp@localhost'" do - expect(command(db_query).stdout).to match /#{priv_query}: Y/ - end - end - - %w{select update insert delete create}.each do |priv| - include_examples "a privilege", priv do - end - end - end - end - end - - describe "generated webpages" do - let(:get_response) { Net::HTTP.get_response(uri) } - shared_examples_for "a webpage" do - it "exists" do - expect(get_response).to be_kind_of(Net::HTTPSuccess) - end - - it "displays content" do - expect(get_response.body).to include(content) - end - end - - describe "http://localhost/index.html" do - include_examples "a webpage" do - let(:uri) { URI.parse("http://localhost/index.html") } - let(:content) { "Hello, World!" } - end - end - - describe "http://localhost/index.php" do - include_examples "a webpage" do - let(:uri) { URI.parse("http://localhost/index.php") } - let(:content) { "Hello, World!" } - end - end - end -end diff --git a/kitchen-tests/vendor/bundle/bundler/gems/kitchen-ec2-fec3f199a646 b/kitchen-tests/vendor/bundle/bundler/gems/kitchen-ec2-fec3f199a646 new file mode 160000 +Subproject fec3f199a646980dc289ac6db9f90e9a9e4b0f6 diff --git a/lib/chef/application.rb b/lib/chef/application.rb index 7dbffd8dec..f8df71f723 100644 --- a/lib/chef/application.rb +++ b/lib/chef/application.rb @@ -27,6 +27,7 @@ require "chef/platform" require "mixlib/cli" require "tmpdir" require "rbconfig" +require "chef/application/exit_code" class Chef class Application @@ -60,11 +61,11 @@ class Chef def setup_signal_handlers trap("INT") do - Chef::Application.fatal!("SIGINT received, stopping", 2) + Chef::Application.fatal!("SIGINT received, stopping", Chef::Exceptions::SigInt.new) end trap("TERM") do - Chef::Application.fatal!("SIGTERM received, stopping", 3) + Chef::Application.fatal!("SIGTERM received, stopping", Chef::Exceptions::SigTerm.new) end unless Chef::Platform.windows? @@ -149,7 +150,7 @@ class Chef Chef::Log.level = resolve_log_level rescue StandardError => error Chef::Log.fatal("Failed to open or create log file at #{Chef::Config[:log_location]}: #{error.class} (#{error.message})") - Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", 2) + Chef::Application.fatal!("Aborting due to invalid 'log_location' configuration", error) end # Turn `log_location :syslog` and `log_location :win_evt` into the @@ -285,7 +286,7 @@ class Chef @chef_client.run rescue Exception => e Chef::Log.error(e.to_s) - exit 1 + exit Chef::Application.normalize_exit_code(e) else exit 0 end @@ -314,7 +315,7 @@ class Chef Chef::Log.fatal("Configuration error #{error.class}: #{error.message}") filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/) filtered_trace.each { |line| Chef::Log.fatal(" " + line ) } - Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", 2) + Chef::Application.fatal!("Aborting due to error in '#{config_file_path}'", error) end # This is a hook for testing @@ -341,15 +342,19 @@ class Chef true end + def normalize_exit_code(exit_code) + Chef::Application::ExitCode.normalize_exit_code(exit_code) + end + # Log a fatal error message to both STDERR and the Logger, exit the application - def fatal!(msg, err = -1) + def fatal!(msg, err = nil) Chef::Log.fatal(msg) - Process.exit err + Process.exit(normalize_exit_code(err)) end - def exit!(msg, err = -1) + def exit!(msg, err = nil) Chef::Log.debug(msg) - Process.exit err + Process.exit(normalize_exit_code(err)) end end diff --git a/lib/chef/application/apply.rb b/lib/chef/application/apply.rb index 37ddcb3164..3e3fb58448 100644 --- a/lib/chef/application/apply.rb +++ b/lib/chef/application/apply.rb @@ -137,11 +137,11 @@ class Chef::Application::Apply < Chef::Application def read_recipe_file(file_name) if file_name.nil? - Chef::Application.fatal!("No recipe file was provided", 1) + Chef::Application.fatal!("No recipe file was provided", Chef::Exceptions::RecipeNotFound.new) else recipe_path = File.expand_path(file_name) unless File.exist?(recipe_path) - Chef::Application.fatal!("No file exists at #{recipe_path}", 1) + Chef::Application.fatal!("No file exists at #{recipe_path}", Chef::Exceptions::RecipeNotFound.new) end recipe_fh = open(recipe_path) recipe_text = recipe_fh.read @@ -183,7 +183,7 @@ class Chef::Application::Apply < Chef::Application else if !ARGV[0] puts opt_parser - Chef::Application.exit! "No recipe file provided", 1 + Chef::Application.exit! "No recipe file provided", Chef::Exceptions::RecipeNotFound.new end @recipe_filename = ARGV[0] @recipe_text, @recipe_fh = read_recipe_file @recipe_filename @@ -208,7 +208,7 @@ class Chef::Application::Apply < Chef::Application raise rescue Exception => e Chef::Application.debug_stacktrace(e) - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) + Chef::Application.fatal!("#{e.class}: #{e.message}", e) end end diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index ac46e533dd..77c86ad559 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -324,7 +324,7 @@ class Chef::Application::Client < Chef::Application if Chef::Config[:recipe_url] if !Chef::Config.local_mode - Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode", 1) + Chef::Application.fatal!("chef-client recipe-url can be used only in local-mode") else if Chef::Config[:delete_entire_chef_repo] Chef::Log.debug "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it" @@ -420,7 +420,7 @@ class Chef::Application::Client < Chef::Application rescue SystemExit raise rescue Exception => e - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) + Chef::Application.fatal!("#{e.class}: #{e.message}", e) end else interval_run_chef_client @@ -463,7 +463,7 @@ class Chef::Application::Client < Chef::Application retry end - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) + Chef::Application.fatal!("#{e.class}: #{e.message}", e) end def test_signal diff --git a/lib/chef/application/exit_code.rb b/lib/chef/application/exit_code.rb new file mode 100644 index 0000000000..753f1a0d80 --- /dev/null +++ b/lib/chef/application/exit_code.rb @@ -0,0 +1,226 @@ +# +# Author:: Steven Murawski (<smurawski@chef.io>) +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +class Chef + class Application + + # These are the exit codes defined in Chef RFC 062 + # https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md + class ExitCode + + # -1 is defined as DEPRECATED_FAILURE in RFC 062, so it is + # not enumerated in an active constant. + # + VALID_RFC_062_EXIT_CODES = { + SUCCESS: 0, + GENERIC_FAILURE: 1, + SIGINT_RECEIVED: 2, + SIGTERM_RECEIVED: 3, + REBOOT_SCHEDULED: 35, + REBOOT_NEEDED: 37, + REBOOT_FAILED: 41, + AUDIT_MODE_FAILURE: 42, + } + + DEPRECATED_RFC_062_EXIT_CODES = { + DEPRECATED_FAILURE: -1, + } + + class << self + + def normalize_exit_code(exit_code = nil) + if normalization_not_configured? + normalize_legacy_exit_code_with_warning(exit_code) + elsif normalization_disabled? + normalize_legacy_exit_code(exit_code) + else + normalize_exit_code_to_rfc(exit_code) + end + end + + def enforce_rfc_062_exit_codes? + !normalization_disabled? && !normalization_not_configured? + end + + def notify_reboot_exit_code_deprecation + return if normalization_disabled? + notify_on_deprecation(reboot_deprecation_warning) + end + + def notify_deprecated_exit_code + return if normalization_disabled? + notify_on_deprecation(deprecation_warning) + end + + private + + def normalization_disabled? + Chef::Config[:exit_status] == :disabled + end + + def normalization_not_configured? + Chef::Config[:exit_status].nil? + end + + def normalize_legacy_exit_code_with_warning(exit_code) + normalized_exit_code = normalize_legacy_exit_code(exit_code) + unless valid_exit_codes.include? normalized_exit_code + notify_on_deprecation(deprecation_warning) + end + normalized_exit_code + end + + def normalize_legacy_exit_code(exit_code) + case exit_code + when Fixnum + exit_code + when Exception + lookup_exit_code_by_exception(exit_code) + else + default_exit_code + end + end + + def normalize_exit_code_to_rfc(exit_code) + normalized_exit_code = normalize_legacy_exit_code_with_warning(exit_code) + if valid_exit_codes.include? normalized_exit_code + normalized_exit_code + else + VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE] + end + end + + def lookup_exit_code_by_exception(exception) + if sigint_received?(exception) + VALID_RFC_062_EXIT_CODES[:SIGINT_RECEIVED] + elsif sigterm_received?(exception) + VALID_RFC_062_EXIT_CODES[:SIGTERM_RECEIVED] + elsif normalization_disabled? || normalization_not_configured? + if legacy_exit_code?(exception) + # We have lots of "Chef::Application.fatal!('', 2) + # This maintains that behavior at initial introduction + # and when the RFC exit_status compliance is disabled. + VALID_RFC_062_EXIT_CODES[:SIGINT_RECEIVED] + else + VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE] + end + elsif reboot_scheduled?(exception) + VALID_RFC_062_EXIT_CODES[:REBOOT_SCHEDULED] + elsif reboot_needed?(exception) + VALID_RFC_062_EXIT_CODES[:REBOOT_NEEDED] + elsif reboot_failed?(exception) + VALID_RFC_062_EXIT_CODES[:REBOOT_FAILED] + elsif audit_failure?(exception) + VALID_RFC_062_EXIT_CODES[:AUDIT_MODE_FAILURE] + else + VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE] + end + end + + def legacy_exit_code?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::DeprecatedExitCode + end + end + + def reboot_scheduled?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::Reboot + end + end + + def reboot_needed?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::RebootPending + end + end + + def reboot_failed?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::RebootFailed + end + end + + def audit_failure?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::AuditError + end + end + + def sigint_received?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::SigInt + end + end + + def sigterm_received?(exception) + resolve_exception_array(exception).any? do |e| + e.is_a? Chef::Exceptions::SigTerm + end + end + + def resolve_exception_array(exception) + exception_array = [exception] + if exception.respond_to?(:wrapped_errors) + exception.wrapped_errors.each do |e| + exception_array.push e + end + end + exception_array + end + + def valid_exit_codes + VALID_RFC_062_EXIT_CODES.values + end + + def notify_on_deprecation(message) + begin + Chef.log_deprecation(message) + rescue Chef::Exceptions::DeprecatedFeatureError + # Have to rescue this, otherwise this unhandled error preempts + # the current exit code assignment. + end + end + + def deprecation_warning + "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \ + " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \ + " In a future release, non-standard exit codes will be redefined as" \ + " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb." + end + + def reboot_deprecation_warning + "Per RFC 062 (https://github.com/chef/chef-rfc/blob/master/rfc062-exit-status.md)" \ + ", when a reboot is requested Chef Client will exit with an exit code of 35, REBOOT_SCHEDULED." \ + " To maintain the current behavior (an exit code of 0), you will need to set `exit_status` to" \ + " `:disabled` in your client.rb" + end + + def default_exit_code + if normalization_disabled? || normalization_not_configured? + return DEPRECATED_RFC_062_EXIT_CODES[:DEPRECATED_FAILURE] + else + VALID_RFC_062_EXIT_CODES[:GENERIC_FAILURE] + end + end + + end + end + + end +end diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index 9c34d613d8..ecac3f4d4e 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -293,7 +293,7 @@ class Chef::Application::Solo < Chef::Application rescue SystemExit raise rescue Exception => e - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) + Chef::Application.fatal!("#{e.class}: #{e.message}", e) end else interval_run_chef_client @@ -340,7 +340,7 @@ EOH Chef::Log.debug("#{e.class}: #{e}\n#{e.backtrace.join("\n")}") retry else - Chef::Application.fatal!("#{e.class}: #{e.message}", 1) + Chef::Application.fatal!("#{e.class}: #{e.message}", e) end end end diff --git a/lib/chef/application/windows_service.rb b/lib/chef/application/windows_service.rb index fca1ed3689..2f1456ac45 100644 --- a/lib/chef/application/windows_service.rb +++ b/lib/chef/application/windows_service.rb @@ -319,11 +319,11 @@ class Chef Chef::Config.merge!(config) rescue SocketError - Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", 2) + Chef::Application.fatal!("Error getting config file #{Chef::Config[:config_file]}", Chef::Exceptions::DeprecatedExitCode.new) rescue Chef::Exceptions::ConfigurationError => error - Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2) + Chef::Application.fatal!("Error processing config file #{Chef::Config[:config_file]} with error #{error.message}", Chef::Exceptions::DeprecatedExitCode.new) rescue Exception => error - Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}", 2) + Chef::Application.fatal!("Unknown error processing config file #{Chef::Config[:config_file]} with error #{error.message}", Chef::Exceptions::DeprecatedExitCode.new) end end diff --git a/lib/chef/config_fetcher.rb b/lib/chef/config_fetcher.rb index acd2f07f5e..ee1b64956a 100644 --- a/lib/chef/config_fetcher.rb +++ b/lib/chef/config_fetcher.rb @@ -25,7 +25,7 @@ class Chef begin Chef::JSONCompat.from_json(config_data) rescue Chef::Exceptions::JSON::ParseError => error - Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message, 2) + Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message, Chef::Exceptions::DeprecatedExitCode.new) end end @@ -40,15 +40,15 @@ class Chef def fetch_remote_config http.get("") rescue SocketError, SystemCallError, Net::HTTPServerException => error - Chef::Application.fatal!("Cannot fetch config '#{config_location}': '#{error.class}: #{error.message}", 2) + Chef::Application.fatal!("Cannot fetch config '#{config_location}': '#{error.class}: #{error.message}", Chef::Exceptions::DeprecatedExitCode.new) end def read_local_config ::File.read(config_location) rescue Errno::ENOENT - Chef::Application.fatal!("Cannot load configuration from #{config_location}", 2) + Chef::Application.fatal!("Cannot load configuration from #{config_location}", Chef::Exceptions::DeprecatedExitCode.new) rescue Errno::EACCES - Chef::Application.fatal!("Permissions are incorrect on #{config_location}. Please chmod a+r #{config_location}", 2) + Chef::Application.fatal!("Permissions are incorrect on #{config_location}. Please chmod a+r #{config_location}", Chef::Exceptions::DeprecatedExitCode.new) end def config_missing? diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 6afcc9c51e..ea90d80cd8 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -42,6 +42,8 @@ class Chef end class Application < RuntimeError; end + class SigInt < RuntimeError; end + class SigTerm < RuntimeError; end class Cron < RuntimeError; end class Env < RuntimeError; end class Exec < RuntimeError; end @@ -56,6 +58,14 @@ class Chef class UnsupportedAction < RuntimeError; end class MissingLibrary < RuntimeError; end + class DeprecatedExitCode < RuntimeError + def initalize + super "Exiting with a non RFC 062 Exit Code." + require "chef/application/exit_code" + Chef::Application::ExitCode.notify_deprecated_exit_code + end + end + class CannotDetermineNodeName < RuntimeError def initialize super "Unable to determine node name: configure node_name or configure the system's hostname and fqdn" @@ -66,6 +76,9 @@ class Chef class Group < RuntimeError; end class Link < RuntimeError; end class Mount < RuntimeError; end + class Reboot < Exception; end + class RebootPending < Exception; end + class RebootFailed < Mixlib::ShellOut::ShellCommandFailed; end class PrivateKeyMissing < RuntimeError; end class CannotWritePrivateKey < RuntimeError; end class RoleNotFound < RuntimeError; end @@ -426,18 +439,20 @@ This error is most often caused by network issues (proxies, etc) outside of chef end end - class AuditControlGroupDuplicate < RuntimeError + class AuditError < RuntimeError; end + + class AuditControlGroupDuplicate < AuditError def initialize(name) super "Control group with name '#{name}' has already been defined" end end - class AuditNameMissing < RuntimeError; end - class NoAuditsProvided < RuntimeError + class AuditNameMissing < AuditError; end + class NoAuditsProvided < AuditError def initialize super "You must provide a block with controls" end end - class AuditsFailed < RuntimeError + class AuditsFailed < AuditError def initialize(num_failed, num_total) super "Audit phase found failures - #{num_failed}/#{num_total} controls failed" end diff --git a/lib/chef/platform/rebooter.rb b/lib/chef/platform/rebooter.rb index c678b60dd1..74c8b2da1f 100644 --- a/lib/chef/platform/rebooter.rb +++ b/lib/chef/platform/rebooter.rb @@ -19,6 +19,7 @@ require "chef/dsl/reboot_pending" require "chef/log" require "chef/platform" +require "chef/application/exit_code" class Chef class Platform @@ -27,6 +28,8 @@ class Chef class << self + include Chef::DSL::RebootPending + def reboot!(node) reboot_info = node.run_context.reboot_info @@ -38,8 +41,16 @@ class Chef "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\"" end - Chef::Log.warn "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}" - shell_out!(cmd) + msg = "Rebooting server at a recipe's request. Details: #{reboot_info.inspect}" + begin + Chef::Log.warn msg + shell_out!(cmd) + rescue Mixlib::ShellOut::ShellCommandFailed => e + raise Chef::Exceptions::RebootFailed.new(e.message) + end + + raise Chef::Exceptions::Reboot.new(msg) if Chef::Application::ExitCode.enforce_rfc_062_exit_codes? + Chef::Application::ExitCode.notify_reboot_exit_code_deprecation end # this is a wrapper function so Chef::Client only needs a single line of code. diff --git a/lib/chef/provider/package/chocolatey.rb b/lib/chef/provider/package/chocolatey.rb index ebd3f987cd..36bc170590 100644 --- a/lib/chef/provider/package/chocolatey.rb +++ b/lib/chef/provider/package/chocolatey.rb @@ -229,13 +229,11 @@ EOS def available_packages @available_packages ||= begin - cmd = [ "list -ar #{package_name_array.join ' '}" ] + cmd = [ "list -r #{package_name_array.join ' '}" ] cmd.push( "-source #{new_resource.source}" ) if new_resource.source - parse_list_output(*cmd).each_with_object({}) do |name_version, available| - name, version = name_version - if desired_name_versions[name].nil? || desired_name_versions[name] == version - available[name] = version - end + raw = parse_list_output(*cmd) + raw.keys.each_with_object({}) do |name, available| + available[name] = desired_name_versions[name] || raw[name] end end end @@ -252,15 +250,15 @@ EOS # (names are downcased for case-insenstive matching) # # @param cmd [String] command to run - # @return [Array] list output converted to ruby Hash + # @return [Hash] list output converted to ruby Hash def parse_list_output(*args) - list = [] + hash = {} choco_command(*args).stdout.each_line do |line| next if line.start_with?("Chocolatey v") name, version = line.split("|") - list << [ name.downcase, version.chomp ] + hash[name.downcase] = version.chomp end - list + hash end # Helper to downcase all names in an array diff --git a/lib/chef/provider/package/portage.rb b/lib/chef/provider/package/portage.rb index 7c52e43bff..52b46b04b4 100644 --- a/lib/chef/provider/package/portage.rb +++ b/lib/chef/provider/package/portage.rb @@ -41,7 +41,7 @@ class Chef globsafe_pkg = Chef::Util::PathHelper.escape_glob_dir(pkg) possibilities = Dir["/var/db/pkg/#{globsafe_category || "*"}/#{globsafe_pkg}-*"].map { |d| d.sub(%r{/var/db/pkg/}, "") } versions = possibilities.map do |entry| - if entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)} + if entry =~ %r{[^/]+/#{Regexp.escape(pkg)}\-(\d[\.\d]*[a-z]?((_(alpha|beta|pre|rc|p)\d*)*)?(-r\d+)?)} [$&, $1] end end.compact diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index 64f1b2043c..74d52946f7 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -18,10 +18,10 @@ require "chef/config" require "chef/provider/package" -require "chef/mixin/which" -require "chef/resource/package" -require "singleton" +require "chef/resource/yum_package" require "chef/mixin/get_source_from_package" +require "chef/provider/package/yum/rpm_utils" +require "chef/provider/package/yum/yum_cache" class Chef class Provider @@ -31,961 +31,6 @@ class Chef provides :package, platform_family: %w{rhel fedora} provides :yum_package, os: "linux" - class RPMUtils - class << self - - # RPM::Version version_parse equivalent - def version_parse(evr) - return if evr.nil? - - epoch = nil - # assume this is a version - version = evr - release = nil - - lead = 0 - tail = evr.size - - if %r{^([\d]+):}.match(evr) # rubocop:disable Performance/RedundantMatch - epoch = $1.to_i - lead = $1.length + 1 - elsif evr[0].ord == ":".ord - epoch = 0 - lead = 1 - end - - if %r{:?.*-(.*)$}.match(evr) # rubocop:disable Performance/RedundantMatch - release = $1 - tail = evr.length - release.length - lead - 1 - - if release.empty? - release = nil - end - end - - version = evr[lead, tail] - if version.empty? - version = nil - end - - [ epoch, version, release ] - end - - # verify - def isalnum(x) - isalpha(x) || isdigit(x) - end - - def isalpha(x) - v = x.ord - (v >= 65 && v <= 90) || (v >= 97 && v <= 122) - end - - def isdigit(x) - v = x.ord - v >= 48 && v <= 57 - end - - # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0 - def rpmvercmp(x, y) - # easy! :) - return 0 if x == y - - if x.nil? - x = "" - end - - if y.nil? - y = "" - end - - # not so easy :( - # - # takes 2 strings like - # - # x = "1.20.b18.el5" - # y = "1.20.b17.el5" - # - # breaks into purely alpha and numeric segments and compares them using - # some rules - # - # * 10 > 1 - # * 1 > a - # * z > a - # * Z > A - # * z > Z - # * leading zeros are ignored - # * separators (periods, commas) are ignored - # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5" - - x_pos = 0 # overall string element reference position - x_pos_max = x.length - 1 # number of elements in string, starting from 0 - x_seg_pos = 0 # segment string element reference position - x_comp = nil # segment to compare - - y_pos = 0 - y_seg_pos = 0 - y_pos_max = y.length - 1 - y_comp = nil - - while x_pos <= x_pos_max && y_pos <= y_pos_max - # first we skip over anything non alphanumeric - while (x_pos <= x_pos_max) && (isalnum(x[x_pos]) == false) - x_pos += 1 # +1 over pos_max if end of string - end - while (y_pos <= y_pos_max) && (isalnum(y[y_pos]) == false) - y_pos += 1 - end - - # if we hit the end of either we are done matching segments - if (x_pos == x_pos_max + 1) || (y_pos == y_pos_max + 1) - break - end - - # we are now at the start of a alpha or numeric segment - x_seg_pos = x_pos - y_seg_pos = y_pos - - # grab segment so we can compare them - if isdigit(x[x_seg_pos].ord) - x_seg_is_num = true - - # already know it's a digit - x_seg_pos += 1 - - # gather up our digits - while (x_seg_pos <= x_pos_max) && isdigit(x[x_seg_pos]) - x_seg_pos += 1 - end - # copy the segment but not the unmatched character that x_seg_pos will - # refer to - x_comp = x[x_pos, x_seg_pos - x_pos] - - while (y_seg_pos <= y_pos_max) && isdigit(y[y_seg_pos]) - y_seg_pos += 1 - end - y_comp = y[y_pos, y_seg_pos - y_pos] - else - # we are comparing strings - x_seg_is_num = false - - while (x_seg_pos <= x_pos_max) && isalpha(x[x_seg_pos]) - x_seg_pos += 1 - end - x_comp = x[x_pos, x_seg_pos - x_pos] - - while (y_seg_pos <= y_pos_max) && isalpha(y[y_seg_pos]) - y_seg_pos += 1 - end - y_comp = y[y_pos, y_seg_pos - y_pos] - end - - # if y_seg_pos didn't advance in the above loop it means the segments are - # different types - if y_pos == y_seg_pos - # numbers always win over letters - return x_seg_is_num ? 1 : -1 - end - - # move the ball forward before we mess with the segments - x_pos += x_comp.length # +1 over pos_max if end of string - y_pos += y_comp.length - - # we are comparing numbers - simply convert them - if x_seg_is_num - x_comp = x_comp.to_i - y_comp = y_comp.to_i - end - - # compares ints or strings - # don't return if equal - try the next segment - if x_comp > y_comp - return 1 - elsif x_comp < y_comp - return -1 - end - - # if we've reached here than the segments are the same - try again - end - - # we must have reached the end of one or both of the strings and they - # matched up until this point - - # segments matched completely but the segment separators were different - - # rpm reference code treats these as equal. - if (x_pos == x_pos_max + 1) && (y_pos == y_pos_max + 1) - return 0 - end - - # the most unprocessed characters left wins - if (x_pos_max - x_pos) > (y_pos_max - y_pos) - return 1 - else - return -1 - end - end - - end # self - end # RPMUtils - - class RPMVersion - include Comparable - - def initialize(*args) - if args.size == 1 - @e, @v, @r = RPMUtils.version_parse(args[0]) - elsif args.size == 3 - @e = args[0].to_i - @v = args[1] - @r = args[2] - else - raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " + - "version, release'" - end - end - attr_reader :e, :v, :r - alias :epoch :e - alias :version :v - alias :release :r - - def self.parse(*args) - self.new(*args) - end - - def <=>(y) - compare_versions(y) - end - - def compare(y) - compare_versions(y, false) - end - - def partial_compare(y) - compare_versions(y, true) - end - - # RPM::Version rpm_version_to_s equivalent - def to_s - if @r.nil? - @v - else - "#{@v}-#{@r}" - end - end - - def evr - "#{@e}:#{@v}-#{@r}" - end - - private - - # Rough RPM::Version rpm_version_cmp equivalent - except much slower :) - # - # partial lets epoch and version segment equality be good enough to return equal, eg: - # - # 2:1.2-1 == 2:1.2 - # 2:1.2-1 == 2: - # - def compare_versions(y, partial = false) - x = self - - # compare epoch - if (x.e.nil? == false && x.e > 0) && y.e.nil? - return 1 - elsif x.e.nil? && (y.e.nil? == false && y.e > 0) - return -1 - elsif x.e.nil? == false && y.e.nil? == false - if x.e < y.e - return -1 - elsif x.e > y.e - return 1 - end - end - - # compare version - if partial && (x.v.nil? || y.v.nil?) - return 0 - elsif x.v.nil? == false && y.v.nil? - return 1 - elsif x.v.nil? && y.v.nil? == false - return -1 - elsif x.v.nil? == false && y.v.nil? == false - cmp = RPMUtils.rpmvercmp(x.v, y.v) - return cmp if cmp != 0 - end - - # compare release - if partial && (x.r.nil? || y.r.nil?) - return 0 - elsif x.r.nil? == false && y.r.nil? - return 1 - elsif x.r.nil? && y.r.nil? == false - return -1 - elsif x.r.nil? == false && y.r.nil? == false - cmp = RPMUtils.rpmvercmp(x.r, y.r) - return cmp - end - - return 0 - end - end - - class RPMPackage - include Comparable - - def initialize(*args) - if args.size == 4 - @n = args[0] - @version = RPMVersion.new(args[1]) - @a = args[2] - @provides = args[3] - elsif args.size == 6 - @n = args[0] - e = args[1].to_i - v = args[2] - r = args[3] - @version = RPMVersion.new(e, v, r) - @a = args[4] - @provides = args[5] - else - raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " + - "or 'name, epoch, version, release, arch, provides'" - end - - # We always have one, ourselves! - if @provides.empty? - @provides = [ RPMProvide.new(@n, @version.evr, :==) ] - end - end - attr_reader :n, :a, :version, :provides - alias :name :n - alias :arch :a - - def <=>(y) - compare(y) - end - - def compare(y) - x = self - - # easy! :) - return 0 if x.nevra == y.nevra - - # compare name - if x.n.nil? == false && y.n.nil? - return 1 - elsif x.n.nil? && y.n.nil? == false - return -1 - elsif x.n.nil? == false && y.n.nil? == false - if x.n < y.n - return -1 - elsif x.n > y.n - return 1 - end - end - - # compare version - if x.version > y.version - return 1 - elsif x.version < y.version - return -1 - end - - # compare arch - if x.a.nil? == false && y.a.nil? - return 1 - elsif x.a.nil? && y.a.nil? == false - return -1 - elsif x.a.nil? == false && y.a.nil? == false - if x.a < y.a - return -1 - elsif x.a > y.a - return 1 - end - end - - return 0 - end - - def to_s - nevra - end - - def nevra - "#{@n}-#{@version.evr}.#{@a}" - end - end - - # Simple implementation from rpm and ruby-rpm reference code - class RPMDependency - def initialize(*args) - if args.size == 3 - @name = args[0] - @version = RPMVersion.new(args[1]) - # Our requirement to other dependencies - @flag = args[2] || :== - elsif args.size == 5 - @name = args[0] - e = args[1].to_i - v = args[2] - r = args[3] - @version = RPMVersion.new(e, v, r) - @flag = args[4] || :== - else - raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " + - "'name, epoch, version, release, flag'" - end - end - attr_reader :name, :version, :flag - - # Parses 2 forms: - # - # "mtr >= 2:0.71-3.0" - # "mta" - def self.parse(string) - if %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}.match(string) # rubocop:disable Performance/RedundantMatch - name = $1 - if $2 == "=" - flag = :== - else - flag = :"#{$2}" - end - version = $3 - - return self.new(name, version, flag) - else - name = string - return self.new(name, nil, nil) - end - end - - # Test if another RPMDependency satisfies our requirements - def satisfy?(y) - unless y.kind_of?(RPMDependency) - raise ArgumentError, "Expecting an RPMDependency object" - end - - x = self - - # Easy! - if x.name != y.name - return false - end - - # Partial compare - # - # eg: x.version 2.3 == y.version 2.3-1 - sense = x.version.partial_compare(y.version) - - # Thanks to rpmdsCompare() rpmds.c - if (sense < 0) && ((x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<)) - return true - elsif (sense > 0) && ((x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>)) - return true - elsif sense == 0 && ( - ((x.flag == :== || x.flag == :<= || x.flag == :>=) && (y.flag == :== || y.flag == :<= || y.flag == :>=)) || - (x.flag == :< && y.flag == :<) || - (x.flag == :> && y.flag == :>) - ) - return true - end - - return false - end - end - - class RPMProvide < RPMDependency; end - class RPMRequire < RPMDependency; end - - class RPMDbPackage < RPMPackage - # <rpm parts>, installed, available - def initialize(*args) - @repoid = args.pop - # state - @available = args.pop - @installed = args.pop - super(*args) - end - attr_reader :repoid, :available, :installed - end - - # Simple storage for RPMPackage objects - keeps them unique and sorted - class RPMDb - def initialize - # package name => [ RPMPackage, RPMPackage ] of different versions - @rpms = Hash.new - # package nevra => RPMPackage for lookups - @index = Hash.new - # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature - @provides = Hash.new - # RPMPackages listed as available - @available = Set.new - # RPMPackages listed as installed - @installed = Set.new - end - - def [](package_name) - self.lookup(package_name) - end - - # Lookup package_name and return a descending array of package objects - def lookup(package_name) - pkgs = @rpms[package_name] - if pkgs - return pkgs.sort.reverse - else - return nil - end - end - - def lookup_provides(provide_name) - @provides[provide_name] - end - - # Using the package name as a key, and nevra for an index, keep a unique list of packages. - # The available/installed state can be overwritten for existing packages. - def push(*args) - args.flatten.each do |new_rpm| - unless new_rpm.kind_of?(RPMDbPackage) - raise ArgumentError, "Expecting an RPMDbPackage object" - end - - @rpms[new_rpm.n] ||= Array.new - - # we may already have this one, like when the installed list is refreshed - idx = @index[new_rpm.nevra] - if idx - # grab the existing package if it's not - curr_rpm = idx - else - @rpms[new_rpm.n] << new_rpm - - new_rpm.provides.each do |provide| - @provides[provide.name] ||= Array.new - @provides[provide.name] << new_rpm - end - - curr_rpm = new_rpm - end - - # Track the nevra -> RPMPackage association to avoid having to compare versions - # with @rpms[new_rpm.n] on the next round - @index[new_rpm.nevra] = curr_rpm - - # these are overwritten for existing packages - if new_rpm.available - @available << curr_rpm - end - if new_rpm.installed - @installed << curr_rpm - end - end - end - - def <<(*args) - self.push(args) - end - - def clear - @rpms.clear - @index.clear - @provides.clear - clear_available - clear_installed - end - - def clear_available - @available.clear - end - - def clear_installed - @installed.clear - end - - def size - @rpms.size - end - alias :length :size - - def available_size - @available.size - end - - def installed_size - @installed.size - end - - def available?(package) - @available.include?(package) - end - - def installed?(package) - @installed.include?(package) - end - - def whatprovides(rpmdep) - unless rpmdep.kind_of?(RPMDependency) - raise ArgumentError, "Expecting an RPMDependency object" - end - - what = [] - - packages = lookup_provides(rpmdep.name) - if packages - packages.each do |pkg| - pkg.provides.each do |provide| - if provide.satisfy?(rpmdep) - what << pkg - end - end - end - end - - return what - end - end - - # Cache for our installed and available packages, pulled in from yum-dump.py - class YumCache - include Chef::Mixin::Which - include Chef::Mixin::ShellOut - include Singleton - - attr_accessor :yum_binary - - def initialize - @rpmdb = RPMDb.new - - # Next time @rpmdb is accessed: - # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates - # yum's cache and parses options from /etc/yum.conf. Pulls in Provides - # dependency data for installed packages only - this data is slow to - # gather. - # :provides - Same as :all but pulls in Provides data for available packages as well. - # Used as a last resort when we can't find a Provides match. - # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm - # db. Used between client runs for a quick refresh. - # :none - Do nothing, a call to one of the reload methods is required. - @next_refresh = :all - - @allow_multi_install = [] - - @extra_repo_control = nil - - # these are for subsequent runs if we are on an interval - Chef::Client.when_run_starts do - YumCache.instance.reload - end - end - - attr_reader :extra_repo_control - - # Cache management - # - - def refresh - case @next_refresh - when :none - return nil - when :installed - reset_installed - # fast - opts = " --installed" - when :all - reset - # medium - opts = " --options --installed-provides" - when :provides - reset - # slow! - opts = " --options --all-provides" - else - raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}" - end - - if @extra_repo_control - opts << " #{@extra_repo_control}" - end - - opts << " --yum-lock-timeout #{Chef::Config[:yum_lock_timeout]}" - - one_line = false - error = nil - - helper = ::File.join(::File.dirname(__FILE__), "yum-dump.py") - status = nil - - begin - status = shell_out!("#{python_bin} #{helper}#{opts}", :timeout => Chef::Config[:yum_timeout]) - status.stdout.each_line do |line| - one_line = true - - line.chomp! - if line =~ %r{\[option (.*)\] (.*)} - if $1 == "installonlypkgs" - @allow_multi_install = $2.split - else - raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py" - end - next - end - - if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r]) (\S+)$} - name = $1 - epoch = $2 - version = $3 - release = $4 - arch = $5 - provides = parse_provides($6) - type = $7 - repoid = $8 - else - Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " + - "Please check your yum configuration.") - next - end - - case type - when "i" - # if yum-dump was called with --installed this may not be true, but it's okay - # since we don't touch the @available Set in reload_installed - available = false - installed = true - when "a" - available = true - installed = false - when "r" - available = true - installed = true - end - - pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available, repoid) - @rpmdb << pkg - end - - error = status.stderr - rescue Mixlib::ShellOut::CommandTimeout => e - Chef::Log.error("#{helper} exceeded timeout #{Chef::Config[:yum_timeout]}") - raise(e) - end - - if status.exitstatus != 0 - raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}" - else - unless one_line - Chef::Log.warn("Odd, no output from yum-dump.py. Please check " + - "your yum configuration.") - end - end - - # A reload method must be called before the cache is altered - @next_refresh = :none - end - - def python_bin - yum_executable = which(yum_binary) - if yum_executable && shabang?(yum_executable) - shabang_or_fallback(extract_interpreter(yum_executable)) - else - Chef::Log.warn("Yum executable not found or doesn't start with #!. Using default python.") - "/usr/bin/python" - end - rescue StandardError => e - Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.") - Chef::Log.debug(e) - "/usr/bin/python" - end - - def extract_interpreter(file) - ::File.open(file, "r", &:readline)[2..-1].strip - end - - # dnf based systems have a yum shim that has /bin/bash as the interpreter. Don't use this. - def shabang_or_fallback(interpreter) - if interpreter == "/bin/bash" - Chef::Log.warn("Yum executable interpreter is /bin/bash. Falling back to default python.") - "/usr/bin/python" - else - interpreter - end - end - - def shabang?(file) - ::File.open(file, "r") do |f| - f.read(2) == '#!' - end - rescue Errno::ENOENT - false - end - - def reload - @next_refresh = :all - end - - def reload_installed - @next_refresh = :installed - end - - def reload_provides - @next_refresh = :provides - end - - def reset - @rpmdb.clear - end - - def reset_installed - @rpmdb.clear_installed - end - - # Querying the cache - # - - # Check for package by name or name+arch - def package_available?(package_name) - refresh - - if @rpmdb.lookup(package_name) - return true - else - if package_name =~ %r{^(.*)\.(.*)$} - pkg_name = $1 - pkg_arch = $2 - - if matches = @rpmdb.lookup(pkg_name) - matches.each do |m| - return true if m.arch == pkg_arch - end - end - end - end - - return false - end - - # Returns a array of packages satisfying an RPMDependency - def packages_from_require(rpmdep) - refresh - @rpmdb.whatprovides(rpmdep) - end - - # Check if a package-version.arch is available to install - def version_available?(package_name, desired_version, arch = nil) - version(package_name, arch, true, false) do |v| - return true if desired_version == v - end - - return false - end - - # Return the source repository for a package-version.arch - def package_repository(package_name, desired_version, arch = nil) - package(package_name, arch, true, false) do |pkg| - return pkg.repoid if desired_version == pkg.version.to_s - end - - return nil - end - - # Return the latest available version for a package.arch - def available_version(package_name, arch = nil) - version(package_name, arch, true, false) - end - alias :candidate_version :available_version - - # Return the currently installed version for a package.arch - def installed_version(package_name, arch = nil) - version(package_name, arch, false, true) - end - - # Return an array of packages allowed to be installed multiple times, such as the kernel - def allow_multi_install - refresh - @allow_multi_install - end - - def enable_extra_repo_control(arg) - # Don't touch cache if it's the same repos as the last load - unless @extra_repo_control == arg - @extra_repo_control = arg - reload - end - end - - def disable_extra_repo_control - # Only force reload when set - if @extra_repo_control - @extra_repo_control = nil - reload - end - end - - private - - def version(package_name, arch = nil, is_available = false, is_installed = false) - package(package_name, arch, is_available, is_installed) do |pkg| - if block_given? - yield pkg.version.to_s - else - # first match is latest version - return pkg.version.to_s - end - end - - if block_given? - return self - else - return nil - end - end - - def package(package_name, arch = nil, is_available = false, is_installed = false) - refresh - packages = @rpmdb[package_name] - if packages - packages.each do |pkg| - if is_available - next unless @rpmdb.available?(pkg) - end - if is_installed - next unless @rpmdb.installed?(pkg) - end - if arch - next unless pkg.arch == arch - end - - if block_given? - yield pkg - else - # first match is latest version - return pkg - end - end - end - - if block_given? - return self - else - return nil - end - end - - # Parse provides from yum-dump.py output - def parse_provides(string) - ret = [] - # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0'] - string.split(", ").each do |seg| - # 'atk = 1.12.2-1.fc6' - if seg =~ %r{^'(.*)'$} - ret << RPMProvide.parse($1) - end - end - - return ret - end - - end # YumCache - include Chef::Mixin::GetSourceFromPackage def initialize(new_resource, run_context) @@ -1126,7 +171,7 @@ class Chef end end - @current_resource = Chef::Resource::Package.new(@new_resource.name) + @current_resource = Chef::Resource::YumPackage.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) installed_version = [] diff --git a/lib/chef/provider/package/yum/rpm_utils.rb b/lib/chef/provider/package/yum/rpm_utils.rb new file mode 100644 index 0000000000..a748c664a9 --- /dev/null +++ b/lib/chef/provider/package/yum/rpm_utils.rb @@ -0,0 +1,642 @@ + +# Author:: Adam Jacob (<adam@chef.io>) +# Copyright:: Copyright 2008-2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/provider/package" + +class Chef + class Provider + class Package + class Yum < Chef::Provider::Package + class RPMUtils + class << self + + # RPM::Version version_parse equivalent + def version_parse(evr) + return if evr.nil? + + epoch = nil + # assume this is a version + version = evr + release = nil + + lead = 0 + tail = evr.size + + if %r{^([\d]+):}.match(evr) # rubocop:disable Performance/RedundantMatch + epoch = $1.to_i + lead = $1.length + 1 + elsif evr[0].ord == ":".ord + epoch = 0 + lead = 1 + end + + if %r{:?.*-(.*)$}.match(evr) # rubocop:disable Performance/RedundantMatch + release = $1 + tail = evr.length - release.length - lead - 1 + + if release.empty? + release = nil + end + end + + version = evr[lead, tail] + if version.empty? + version = nil + end + + [ epoch, version, release ] + end + + # verify + def isalnum(x) + isalpha(x) || isdigit(x) + end + + def isalpha(x) + v = x.ord + (v >= 65 && v <= 90) || (v >= 97 && v <= 122) + end + + def isdigit(x) + v = x.ord + v >= 48 && v <= 57 + end + + # based on the reference spec in lib/rpmvercmp.c in rpm 4.9.0 + def rpmvercmp(x, y) + # easy! :) + return 0 if x == y + + if x.nil? + x = "" + end + + if y.nil? + y = "" + end + + # not so easy :( + # + # takes 2 strings like + # + # x = "1.20.b18.el5" + # y = "1.20.b17.el5" + # + # breaks into purely alpha and numeric segments and compares them using + # some rules + # + # * 10 > 1 + # * 1 > a + # * z > a + # * Z > A + # * z > Z + # * leading zeros are ignored + # * separators (periods, commas) are ignored + # * "1.20.b18.el5.extrastuff" > "1.20.b18.el5" + + x_pos = 0 # overall string element reference position + x_pos_max = x.length - 1 # number of elements in string, starting from 0 + x_seg_pos = 0 # segment string element reference position + x_comp = nil # segment to compare + + y_pos = 0 + y_seg_pos = 0 + y_pos_max = y.length - 1 + y_comp = nil + + while x_pos <= x_pos_max && y_pos <= y_pos_max + # first we skip over anything non alphanumeric + while (x_pos <= x_pos_max) && (isalnum(x[x_pos]) == false) + x_pos += 1 # +1 over pos_max if end of string + end + while (y_pos <= y_pos_max) && (isalnum(y[y_pos]) == false) + y_pos += 1 + end + + # if we hit the end of either we are done matching segments + if (x_pos == x_pos_max + 1) || (y_pos == y_pos_max + 1) + break + end + + # we are now at the start of a alpha or numeric segment + x_seg_pos = x_pos + y_seg_pos = y_pos + + # grab segment so we can compare them + if isdigit(x[x_seg_pos].ord) + x_seg_is_num = true + + # already know it's a digit + x_seg_pos += 1 + + # gather up our digits + while (x_seg_pos <= x_pos_max) && isdigit(x[x_seg_pos]) + x_seg_pos += 1 + end + # copy the segment but not the unmatched character that x_seg_pos will + # refer to + x_comp = x[x_pos, x_seg_pos - x_pos] + + while (y_seg_pos <= y_pos_max) && isdigit(y[y_seg_pos]) + y_seg_pos += 1 + end + y_comp = y[y_pos, y_seg_pos - y_pos] + else + # we are comparing strings + x_seg_is_num = false + + while (x_seg_pos <= x_pos_max) && isalpha(x[x_seg_pos]) + x_seg_pos += 1 + end + x_comp = x[x_pos, x_seg_pos - x_pos] + + while (y_seg_pos <= y_pos_max) && isalpha(y[y_seg_pos]) + y_seg_pos += 1 + end + y_comp = y[y_pos, y_seg_pos - y_pos] + end + + # if y_seg_pos didn't advance in the above loop it means the segments are + # different types + if y_pos == y_seg_pos + # numbers always win over letters + return x_seg_is_num ? 1 : -1 + end + + # move the ball forward before we mess with the segments + x_pos += x_comp.length # +1 over pos_max if end of string + y_pos += y_comp.length + + # we are comparing numbers - simply convert them + if x_seg_is_num + x_comp = x_comp.to_i + y_comp = y_comp.to_i + end + + # compares ints or strings + # don't return if equal - try the next segment + if x_comp > y_comp + return 1 + elsif x_comp < y_comp + return -1 + end + + # if we've reached here than the segments are the same - try again + end + + # we must have reached the end of one or both of the strings and they + # matched up until this point + + # segments matched completely but the segment separators were different - + # rpm reference code treats these as equal. + if (x_pos == x_pos_max + 1) && (y_pos == y_pos_max + 1) + return 0 + end + + # the most unprocessed characters left wins + if (x_pos_max - x_pos) > (y_pos_max - y_pos) + return 1 + else + return -1 + end + end + + end # self + end # RPMUtils + + class RPMVersion + include Comparable + + def initialize(*args) + if args.size == 1 + @e, @v, @r = RPMUtils.version_parse(args[0]) + elsif args.size == 3 + @e = args[0].to_i + @v = args[1] + @r = args[2] + else + raise ArgumentError, "Expecting either 'epoch-version-release' or 'epoch, " + + "version, release'" + end + end + attr_reader :e, :v, :r + alias :epoch :e + alias :version :v + alias :release :r + + def self.parse(*args) + self.new(*args) + end + + def <=>(y) + compare_versions(y) + end + + def compare(y) + compare_versions(y, false) + end + + def partial_compare(y) + compare_versions(y, true) + end + + # RPM::Version rpm_version_to_s equivalent + def to_s + if @r.nil? + @v + else + "#{@v}-#{@r}" + end + end + + def evr + "#{@e}:#{@v}-#{@r}" + end + + private + + # Rough RPM::Version rpm_version_cmp equivalent - except much slower :) + # + # partial lets epoch and version segment equality be good enough to return equal, eg: + # + # 2:1.2-1 == 2:1.2 + # 2:1.2-1 == 2: + # + def compare_versions(y, partial = false) + x = self + + # compare epoch + if (x.e.nil? == false && x.e > 0) && y.e.nil? + return 1 + elsif x.e.nil? && (y.e.nil? == false && y.e > 0) + return -1 + elsif x.e.nil? == false && y.e.nil? == false + if x.e < y.e + return -1 + elsif x.e > y.e + return 1 + end + end + + # compare version + if partial && (x.v.nil? || y.v.nil?) + return 0 + elsif x.v.nil? == false && y.v.nil? + return 1 + elsif x.v.nil? && y.v.nil? == false + return -1 + elsif x.v.nil? == false && y.v.nil? == false + cmp = RPMUtils.rpmvercmp(x.v, y.v) + return cmp if cmp != 0 + end + + # compare release + if partial && (x.r.nil? || y.r.nil?) + return 0 + elsif x.r.nil? == false && y.r.nil? + return 1 + elsif x.r.nil? && y.r.nil? == false + return -1 + elsif x.r.nil? == false && y.r.nil? == false + cmp = RPMUtils.rpmvercmp(x.r, y.r) + return cmp + end + + return 0 + end + end + + class RPMPackage + include Comparable + + def initialize(*args) + if args.size == 4 + @n = args[0] + @version = RPMVersion.new(args[1]) + @a = args[2] + @provides = args[3] + elsif args.size == 6 + @n = args[0] + e = args[1].to_i + v = args[2] + r = args[3] + @version = RPMVersion.new(e, v, r) + @a = args[4] + @provides = args[5] + else + raise ArgumentError, "Expecting either 'name, epoch-version-release, arch, provides' " + + "or 'name, epoch, version, release, arch, provides'" + end + + # We always have one, ourselves! + if @provides.empty? + @provides = [ RPMProvide.new(@n, @version.evr, :==) ] + end + end + attr_reader :n, :a, :version, :provides + alias :name :n + alias :arch :a + + def <=>(y) + compare(y) + end + + def compare(y) + x = self + + # easy! :) + return 0 if x.nevra == y.nevra + + # compare name + if x.n.nil? == false && y.n.nil? + return 1 + elsif x.n.nil? && y.n.nil? == false + return -1 + elsif x.n.nil? == false && y.n.nil? == false + if x.n < y.n + return -1 + elsif x.n > y.n + return 1 + end + end + + # compare version + if x.version > y.version + return 1 + elsif x.version < y.version + return -1 + end + + # compare arch + if x.a.nil? == false && y.a.nil? + return 1 + elsif x.a.nil? && y.a.nil? == false + return -1 + elsif x.a.nil? == false && y.a.nil? == false + if x.a < y.a + return -1 + elsif x.a > y.a + return 1 + end + end + + return 0 + end + + def to_s + nevra + end + + def nevra + "#{@n}-#{@version.evr}.#{@a}" + end + end + + # Simple implementation from rpm and ruby-rpm reference code + class RPMDependency + def initialize(*args) + if args.size == 3 + @name = args[0] + @version = RPMVersion.new(args[1]) + # Our requirement to other dependencies + @flag = args[2] || :== + elsif args.size == 5 + @name = args[0] + e = args[1].to_i + v = args[2] + r = args[3] + @version = RPMVersion.new(e, v, r) + @flag = args[4] || :== + else + raise ArgumentError, "Expecting either 'name, epoch-version-release, flag' or " + + "'name, epoch, version, release, flag'" + end + end + attr_reader :name, :version, :flag + + # Parses 2 forms: + # + # "mtr >= 2:0.71-3.0" + # "mta" + def self.parse(string) + if %r{^(\S+)\s+(>|>=|=|==|<=|<)\s+(\S+)$}.match(string) # rubocop:disable Performance/RedundantMatch + name = $1 + if $2 == "=" + flag = :== + else + flag = :"#{$2}" + end + version = $3 + + return self.new(name, version, flag) + else + name = string + return self.new(name, nil, nil) + end + end + + # Test if another RPMDependency satisfies our requirements + def satisfy?(y) + unless y.kind_of?(RPMDependency) + raise ArgumentError, "Expecting an RPMDependency object" + end + + x = self + + # Easy! + if x.name != y.name + return false + end + + # Partial compare + # + # eg: x.version 2.3 == y.version 2.3-1 + sense = x.version.partial_compare(y.version) + + # Thanks to rpmdsCompare() rpmds.c + if (sense < 0) && ((x.flag == :> || x.flag == :>=) || (y.flag == :<= || y.flag == :<)) + return true + elsif (sense > 0) && ((x.flag == :< || x.flag == :<=) || (y.flag == :>= || y.flag == :>)) + return true + elsif sense == 0 && ( + ((x.flag == :== || x.flag == :<= || x.flag == :>=) && (y.flag == :== || y.flag == :<= || y.flag == :>=)) || + (x.flag == :< && y.flag == :<) || + (x.flag == :> && y.flag == :>) + ) + return true + end + + return false + end + end + + class RPMProvide < RPMDependency; end + class RPMRequire < RPMDependency; end + + class RPMDbPackage < RPMPackage + # <rpm parts>, installed, available + def initialize(*args) + @repoid = args.pop + # state + @available = args.pop + @installed = args.pop + super(*args) + end + attr_reader :repoid, :available, :installed + end + + # Simple storage for RPMPackage objects - keeps them unique and sorted + class RPMDb + def initialize + # package name => [ RPMPackage, RPMPackage ] of different versions + @rpms = Hash.new + # package nevra => RPMPackage for lookups + @index = Hash.new + # provide name (aka feature) => [RPMPackage, RPMPackage] each providing this feature + @provides = Hash.new + # RPMPackages listed as available + @available = Set.new + # RPMPackages listed as installed + @installed = Set.new + end + + def [](package_name) + self.lookup(package_name) + end + + # Lookup package_name and return a descending array of package objects + def lookup(package_name) + pkgs = @rpms[package_name] + if pkgs + return pkgs.sort.reverse + else + return nil + end + end + + def lookup_provides(provide_name) + @provides[provide_name] + end + + # Using the package name as a key, and nevra for an index, keep a unique list of packages. + # The available/installed state can be overwritten for existing packages. + def push(*args) + args.flatten.each do |new_rpm| + unless new_rpm.kind_of?(RPMDbPackage) + raise ArgumentError, "Expecting an RPMDbPackage object" + end + + @rpms[new_rpm.n] ||= Array.new + + # we may already have this one, like when the installed list is refreshed + idx = @index[new_rpm.nevra] + if idx + # grab the existing package if it's not + curr_rpm = idx + else + @rpms[new_rpm.n] << new_rpm + + new_rpm.provides.each do |provide| + @provides[provide.name] ||= Array.new + @provides[provide.name] << new_rpm + end + + curr_rpm = new_rpm + end + + # Track the nevra -> RPMPackage association to avoid having to compare versions + # with @rpms[new_rpm.n] on the next round + @index[new_rpm.nevra] = curr_rpm + + # these are overwritten for existing packages + if new_rpm.available + @available << curr_rpm + end + if new_rpm.installed + @installed << curr_rpm + end + end + end + + def <<(*args) + self.push(args) + end + + def clear + @rpms.clear + @index.clear + @provides.clear + clear_available + clear_installed + end + + def clear_available + @available.clear + end + + def clear_installed + @installed.clear + end + + def size + @rpms.size + end + alias :length :size + + def available_size + @available.size + end + + def installed_size + @installed.size + end + + def available?(package) + @available.include?(package) + end + + def installed?(package) + @installed.include?(package) + end + + def whatprovides(rpmdep) + unless rpmdep.kind_of?(RPMDependency) + raise ArgumentError, "Expecting an RPMDependency object" + end + + what = [] + + packages = lookup_provides(rpmdep.name) + if packages + packages.each do |pkg| + pkg.provides.each do |provide| + if provide.satisfy?(rpmdep) + what << pkg + end + end + end + end + + return what + end + end + + end + end + end +end diff --git a/lib/chef/provider/package/yum-dump.py b/lib/chef/provider/package/yum/yum-dump.py index 6183460195..6183460195 100644 --- a/lib/chef/provider/package/yum-dump.py +++ b/lib/chef/provider/package/yum/yum-dump.py diff --git a/lib/chef/provider/package/yum/yum_cache.rb b/lib/chef/provider/package/yum/yum_cache.rb new file mode 100644 index 0000000000..fb25a91c8c --- /dev/null +++ b/lib/chef/provider/package/yum/yum_cache.rb @@ -0,0 +1,376 @@ + +# Author:: Adam Jacob (<adam@chef.io>) +# Copyright:: Copyright 2008-2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef/config" +require "chef/provider/package" +require "chef/mixin/which" +require "chef/mixin/shell_out" +require "singleton" +require "chef/provider/package/yum/rpm_utils" + +class Chef + class Provider + class Package + class Yum < Chef::Provider::Package + # Cache for our installed and available packages, pulled in from yum-dump.py + class YumCache + include Chef::Mixin::Which + include Chef::Mixin::ShellOut + include Singleton + + attr_accessor :yum_binary + + def initialize + @rpmdb = RPMDb.new + + # Next time @rpmdb is accessed: + # :all - Trigger a run of "yum-dump.py --options --installed-provides", updates + # yum's cache and parses options from /etc/yum.conf. Pulls in Provides + # dependency data for installed packages only - this data is slow to + # gather. + # :provides - Same as :all but pulls in Provides data for available packages as well. + # Used as a last resort when we can't find a Provides match. + # :installed - Trigger a run of "yum-dump.py --installed", only reads the local rpm + # db. Used between client runs for a quick refresh. + # :none - Do nothing, a call to one of the reload methods is required. + @next_refresh = :all + + @allow_multi_install = [] + + @extra_repo_control = nil + + # these are for subsequent runs if we are on an interval + Chef::Client.when_run_starts do + YumCache.instance.reload + end + end + + attr_reader :extra_repo_control + + # Cache management + # + + def yum_dump_path + ::File.join(::File.dirname(__FILE__), "yum-dump.py") + end + + def refresh + case @next_refresh + when :none + return nil + when :installed + reset_installed + # fast + opts = " --installed" + when :all + reset + # medium + opts = " --options --installed-provides" + when :provides + reset + # slow! + opts = " --options --all-provides" + else + raise ArgumentError, "Unexpected value in next_refresh: #{@next_refresh}" + end + + if @extra_repo_control + opts << " #{@extra_repo_control}" + end + + opts << " --yum-lock-timeout #{Chef::Config[:yum_lock_timeout]}" + + one_line = false + error = nil + + status = nil + + begin + status = shell_out!("#{python_bin} #{yum_dump_path}#{opts}", :timeout => Chef::Config[:yum_timeout]) + status.stdout.each_line do |line| + one_line = true + + line.chomp! + if line =~ %r{\[option (.*)\] (.*)} + if $1 == "installonlypkgs" + @allow_multi_install = $2.split + else + raise Chef::Exceptions::Package, "Strange, unknown option line '#{line}' from yum-dump.py" + end + next + end + + if line =~ %r{^(\S+) ([0-9]+) (\S+) (\S+) (\S+) \[(.*)\] ([i,a,r]) (\S+)$} + name = $1 + epoch = $2 + version = $3 + release = $4 + arch = $5 + provides = parse_provides($6) + type = $7 + repoid = $8 + else + Chef::Log.warn("Problem parsing line '#{line}' from yum-dump.py! " + + "Please check your yum configuration.") + next + end + + case type + when "i" + # if yum-dump was called with --installed this may not be true, but it's okay + # since we don't touch the @available Set in reload_installed + available = false + installed = true + when "a" + available = true + installed = false + when "r" + available = true + installed = true + end + + pkg = RPMDbPackage.new(name, epoch, version, release, arch, provides, installed, available, repoid) + @rpmdb << pkg + end + + error = status.stderr + rescue Mixlib::ShellOut::CommandTimeout => e + Chef::Log.error("#{yum_dump_path} exceeded timeout #{Chef::Config[:yum_timeout]}") + raise(e) + end + + if status.exitstatus != 0 + raise Chef::Exceptions::Package, "Yum failed - #{status.inspect} - returns: #{error}" + else + unless one_line + Chef::Log.warn("Odd, no output from yum-dump.py. Please check " + + "your yum configuration.") + end + end + + # A reload method must be called before the cache is altered + @next_refresh = :none + end + + def python_bin + yum_executable = which(yum_binary) + if yum_executable && shabang?(yum_executable) + shabang_or_fallback(extract_interpreter(yum_executable)) + else + Chef::Log.warn("Yum executable not found or doesn't start with #!. Using default python.") + "/usr/bin/python" + end + rescue StandardError => e + Chef::Log.warn("An error occurred attempting to determine correct python executable. Using default.") + Chef::Log.debug(e) + "/usr/bin/python" + end + + def extract_interpreter(file) + ::File.open(file, "r", &:readline)[2..-1].strip + end + + # dnf based systems have a yum shim that has /bin/bash as the interpreter. Don't use this. + def shabang_or_fallback(interpreter) + if interpreter == "/bin/bash" + Chef::Log.warn("Yum executable interpreter is /bin/bash. Falling back to default python.") + "/usr/bin/python" + else + interpreter + end + end + + def shabang?(file) + ::File.open(file, "r") do |f| + f.read(2) == '#!' + end + rescue Errno::ENOENT + false + end + + def reload + @next_refresh = :all + end + + def reload_installed + @next_refresh = :installed + end + + def reload_provides + @next_refresh = :provides + end + + def reset + @rpmdb.clear + end + + def reset_installed + @rpmdb.clear_installed + end + + # Querying the cache + # + + # Check for package by name or name+arch + def package_available?(package_name) + refresh + + if @rpmdb.lookup(package_name) + return true + else + if package_name =~ %r{^(.*)\.(.*)$} + pkg_name = $1 + pkg_arch = $2 + + if matches = @rpmdb.lookup(pkg_name) + matches.each do |m| + return true if m.arch == pkg_arch + end + end + end + end + + return false + end + + # Returns a array of packages satisfying an RPMDependency + def packages_from_require(rpmdep) + refresh + @rpmdb.whatprovides(rpmdep) + end + + # Check if a package-version.arch is available to install + def version_available?(package_name, desired_version, arch = nil) + version(package_name, arch, true, false) do |v| + return true if desired_version == v + end + + return false + end + + # Return the source repository for a package-version.arch + def package_repository(package_name, desired_version, arch = nil) + package(package_name, arch, true, false) do |pkg| + return pkg.repoid if desired_version == pkg.version.to_s + end + + return nil + end + + # Return the latest available version for a package.arch + def available_version(package_name, arch = nil) + version(package_name, arch, true, false) + end + alias :candidate_version :available_version + + # Return the currently installed version for a package.arch + def installed_version(package_name, arch = nil) + version(package_name, arch, false, true) + end + + # Return an array of packages allowed to be installed multiple times, such as the kernel + def allow_multi_install + refresh + @allow_multi_install + end + + def enable_extra_repo_control(arg) + # Don't touch cache if it's the same repos as the last load + unless @extra_repo_control == arg + @extra_repo_control = arg + reload + end + end + + def disable_extra_repo_control + # Only force reload when set + if @extra_repo_control + @extra_repo_control = nil + reload + end + end + + private + + def version(package_name, arch = nil, is_available = false, is_installed = false) + package(package_name, arch, is_available, is_installed) do |pkg| + if block_given? + yield pkg.version.to_s + else + # first match is latest version + return pkg.version.to_s + end + end + + if block_given? + return self + else + return nil + end + end + + def package(package_name, arch = nil, is_available = false, is_installed = false) + refresh + packages = @rpmdb[package_name] + if packages + packages.each do |pkg| + if is_available + next unless @rpmdb.available?(pkg) + end + if is_installed + next unless @rpmdb.installed?(pkg) + end + if arch + next unless pkg.arch == arch + end + + if block_given? + yield pkg + else + # first match is latest version + return pkg + end + end + end + + if block_given? + return self + else + return nil + end + end + + # Parse provides from yum-dump.py output + def parse_provides(string) + ret = [] + # ['atk = 1.12.2-1.fc6', 'libatk-1.0.so.0'] + string.split(", ").each do |seg| + # 'atk = 1.12.2-1.fc6' + if seg =~ %r{^'(.*)'$} + ret << RPMProvide.parse($1) + end + end + + return ret + end + + end # YumCache + end + end + end +end diff --git a/lib/chef/provider/systemd_unit.rb b/lib/chef/provider/systemd_unit.rb index a73d0bc0ad..b96a336765 100644 --- a/lib/chef/provider/systemd_unit.rb +++ b/lib/chef/provider/systemd_unit.rb @@ -143,6 +143,24 @@ class Chef end end + def action_try_restart + converge_by("try-restarting unit: #{new_resource.name}") do + systemctl_execute!("try-restart", new_resource.name) + end + end + + def action_reload_or_restart + converge_by("reload-or-restarting unit: #{new_resource.name}") do + systemctl_execute!("reload-or-restart", new_resource.name) + end + end + + def action_reload_or_try_restart + converge_by("reload-or-try-restarting unit: #{new_resource.name}") do + systemctl_execute!("reload-or-try-restart", new_resource.name) + end + end + def active? systemctl_execute("is-active", new_resource.name).exitstatus == 0 end diff --git a/lib/chef/resource/systemd_unit.rb b/lib/chef/resource/systemd_unit.rb index 525e9ab35e..688f2e9dcd 100644 --- a/lib/chef/resource/systemd_unit.rb +++ b/lib/chef/resource/systemd_unit.rb @@ -29,7 +29,9 @@ class Chef :enable, :disable, :mask, :unmask, :start, :stop, - :restart, :reload + :restart, :reload, + :try_restart, :reload_or_restart, + :reload_or_try_restart property :enabled, [TrueClass, FalseClass] property :active, [TrueClass, FalseClass] diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 5d5583cc4f..886415787f 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -21,7 +21,7 @@ class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = "12.10.59" + VERSION = "12.11.12" end # diff --git a/omnibus/config/software/chef-gem-binding_of_caller.rb b/omnibus/config/software/chef-gem-binding_of_caller.rb index a9f8b758c9..ed9083f3b9 100644 --- a/omnibus/config/software/chef-gem-binding_of_caller.rb +++ b/omnibus/config/software/chef-gem-binding_of_caller.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "MIT" +license_file "https://github.com/banister/binding_of_caller/blob/master/LICENSE" diff --git a/omnibus/config/software/chef-gem-byebug.rb b/omnibus/config/software/chef-gem-byebug.rb index a9f8b758c9..f16daa29a0 100644 --- a/omnibus/config/software/chef-gem-byebug.rb +++ b/omnibus/config/software/chef-gem-byebug.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "MIT" +license_file "https://github.com/deivid-rodriguez/byebug/blob/master/LICENSE" diff --git a/omnibus/config/software/chef-gem-debug_inspector.rb b/omnibus/config/software/chef-gem-debug_inspector.rb index a9f8b758c9..a3432a8de3 100644 --- a/omnibus/config/software/chef-gem-debug_inspector.rb +++ b/omnibus/config/software/chef-gem-debug_inspector.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "MIT" +license_file "https://github.com/banister/debug_inspector/blob/master/README.md" diff --git a/omnibus/config/software/chef-gem-ffi-yajl.rb b/omnibus/config/software/chef-gem-ffi-yajl.rb index e8279f7d81..bba55a3882 100644 --- a/omnibus/config/software/chef-gem-ffi-yajl.rb +++ b/omnibus/config/software/chef-gem-ffi-yajl.rb @@ -5,4 +5,7 @@ require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) +license "MIT" +license_file "https://github.com/chef/ffi-yajl/blob/master/LICENSE" + dependency "chef-gem-libyajl2" diff --git a/omnibus/config/software/chef-gem-ffi.rb b/omnibus/config/software/chef-gem-ffi.rb index a9f8b758c9..4d54105a0c 100644 --- a/omnibus/config/software/chef-gem-ffi.rb +++ b/omnibus/config/software/chef-gem-ffi.rb @@ -4,3 +4,8 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "BSD-3-CLAUSE" +license_file "https://github.com/ffi/ffi/blob/master/LICENSE" +license_file "https://github.com/ffi/ffi/blob/master/COPYING" +license_file "https://github.com/ffi/ffi/blob/master/LICENSE.SPECS" diff --git a/omnibus/config/software/chef-gem-json.rb b/omnibus/config/software/chef-gem-json.rb index a9f8b758c9..b2d848c77a 100644 --- a/omnibus/config/software/chef-gem-json.rb +++ b/omnibus/config/software/chef-gem-json.rb @@ -4,3 +4,7 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "Ruby" +license_file "https://github.com/flori/json/blob/master/README.md" +license_file "https://www.ruby-lang.org/en/about/license.txt" diff --git a/omnibus/config/software/chef-gem-libyajl2.rb b/omnibus/config/software/chef-gem-libyajl2.rb index a9f8b758c9..609e3c3089 100644 --- a/omnibus/config/software/chef-gem-libyajl2.rb +++ b/omnibus/config/software/chef-gem-libyajl2.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "Apache-2.0" +license_file "https://github.com/chef/libyajl2-gem/blob/master/LICENSE" diff --git a/omnibus/config/software/chef-gem-mini_portile2.rb b/omnibus/config/software/chef-gem-mini_portile2.rb index a9f8b758c9..9ffa040c14 100644 --- a/omnibus/config/software/chef-gem-mini_portile2.rb +++ b/omnibus/config/software/chef-gem-mini_portile2.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "MIT" +license_file "https://github.com/flavorjones/mini_portile/blob/master/LICENSE.txt" diff --git a/omnibus/config/software/chef-gem-nokogiri.rb b/omnibus/config/software/chef-gem-nokogiri.rb index a18d156e11..8bf7100d5b 100644 --- a/omnibus/config/software/chef-gem-nokogiri.rb +++ b/omnibus/config/software/chef-gem-nokogiri.rb @@ -5,4 +5,7 @@ require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) +license "MIT" +license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE" + dependency "chef-gem-mini_portile2" diff --git a/omnibus/config/software/chef-gem-ruby-prof.rb b/omnibus/config/software/chef-gem-ruby-prof.rb index a9f8b758c9..47ecfea02a 100644 --- a/omnibus/config/software/chef-gem-ruby-prof.rb +++ b/omnibus/config/software/chef-gem-ruby-prof.rb @@ -4,3 +4,6 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "BSD-2-CLAUSE" +license_file "https://github.com/ruby-prof/ruby-prof/blob/master/LICENSE" diff --git a/omnibus/config/software/chef-gem-ruby-shadow.rb b/omnibus/config/software/chef-gem-ruby-shadow.rb index a9f8b758c9..8091fd9460 100644 --- a/omnibus/config/software/chef-gem-ruby-shadow.rb +++ b/omnibus/config/software/chef-gem-ruby-shadow.rb @@ -4,3 +4,7 @@ # another gem's installation. require_relative "../../files/chef-gem/build-chef-gem/gem-install-software-def" BuildChefGem::GemInstallSoftwareDef.define(self, __FILE__) + +license "Public-Domain" +license_file "https://github.com/apalmblad/ruby-shadow/blob/master/LICENSE" +license_file "http://creativecommons.org/licenses/publicdomain/" diff --git a/omnibus_overrides.rb b/omnibus_overrides.rb index 3414f217fa..d971dd99fa 100644 --- a/omnibus_overrides.rb +++ b/omnibus_overrides.rb @@ -1,6 +1,6 @@ # DO NOT EDIT. Generated by "rake dependencies". Edit version_policy.rb instead. override :rubygems, version: "2.6.4" -override :bundler, version: "1.12.3" +override :bundler, version: "1.11.2" override "libffi", version: "3.2.1" override "libiconv", version: "1.14" override "liblzma", version: "5.2.2" diff --git a/spec/functional/resource/chocolatey_package_spec.rb b/spec/functional/resource/chocolatey_package_spec.rb index fb51fd2d64..7bb6698daf 100644 --- a/spec/functional/resource/chocolatey_package_spec.rb +++ b/spec/functional/resource/chocolatey_package_spec.rb @@ -82,11 +82,6 @@ describe Chef::Resource::ChocolateyPackage, :windows_only do subject.package_name "blah" expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package end - - it "raises if package version is not found" do - subject.version "3.0" - expect { subject.run_action(:install) }.to raise_error Chef::Exceptions::Package - end end context "upgrading a package" do diff --git a/spec/integration/client/exit_code_spec.rb b/spec/integration/client/exit_code_spec.rb new file mode 100644 index 0000000000..30020f6a3f --- /dev/null +++ b/spec/integration/client/exit_code_spec.rb @@ -0,0 +1,245 @@ + +require "support/shared/integration/integration_helper" +require "chef/mixin/shell_out" +require "tiny_server" +require "tmpdir" +require "chef/platform" + +describe "chef-client" do + + include IntegrationSupport + include Chef::Mixin::ShellOut + + let(:chef_dir) { File.join(File.dirname(__FILE__), "..", "..", "..", "bin") } + + # Invoke `chef-client` as `ruby PATH/TO/chef-client`. This ensures the + # following constraints are satisfied: + # * Windows: windows can only run batch scripts as bare executables. Rubygems + # creates batch wrappers for installed gems, but we don't have batch wrappers + # in the source tree. + # * Other `chef-client` in PATH: A common case is running the tests on a + # machine that has omnibus chef installed. In that case we need to ensure + # we're running `chef-client` from the source tree and not the external one. + # cf. CHEF-4914 + let(:chef_client) { "ruby '#{chef_dir}/chef-client' --no-fork --minimal-ohai" } + + let(:critical_env_vars) { %w{PATH RUBYOPT BUNDLE_GEMFILE GEM_PATH}.map { |o| "#{o}=#{ENV[o]}" } .join(" ") } + + when_the_repository "does not have exit_status configured" do + + def setup_client_rb + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +EOM + end + + def setup_client_rb_with_audit_mode + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +audit_mode :audit_only +EOM + end + + def run_chef_client_and_expect_exit_code(exit_code) + shell_out!( + "#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", + :cwd => chef_dir, + :returns => [exit_code]) + end + + context "has a cookbook" do + context "with a library" do + context "which cannot be loaded" do + before do + file "cookbooks/x/recipes/default.rb", "" + file "cookbooks/x/libraries/error.rb", "require 'does/not/exist'" + end + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + end + + context "with an audit recipe" do + context "which fails" do + before do + file "cookbooks/x/recipes/default.rb", <<-RECIPE +control_group "control group without top level control" do + it "should fail" do + expect(2 - 2).to eq(1) + end +end +RECIPE + end + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb_with_audit_mode + run_chef_client_and_expect_exit_code 1 + end + end + end + + context "with a recipe" do + context "which throws an error" do + before { file "cookbooks/x/recipes/default.rb", "raise 'BOOM'" } + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + + context "with a recipe which calls Chef::Application.fatal with a non-RFC exit code" do + before { file "cookbooks/x/recipes/default.rb", "Chef::Application.fatal!('BOOM', 123)" } + + it "exits with the specified exit code" do + setup_client_rb + run_chef_client_and_expect_exit_code 123 + end + end + + context "with a recipe which calls Chef::Application.exit with a non-RFC exit code" do + before { file "cookbooks/x/recipes/default.rb", "Chef::Application.exit!('BOOM', 231)" } + + it "exits with the specified exit code" do + setup_client_rb + run_chef_client_and_expect_exit_code 231 + end + end + end + + context "when an attempt to reboot fails (like from the reboot resource)" do + before do + file "cookbooks/x/recipes/default.rb", <<EOM +raise Chef::Exceptions::RebootFailed.new +EOM + end + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + end + end + + when_the_repository "does has exit_status enabled" do + + def setup_client_rb + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +exit_status :enabled +EOM + end + + def setup_client_rb_with_audit_mode + file "config/client.rb", <<EOM +local_mode true +cookbook_path "#{path_to('cookbooks')}" +exit_status :enabled +audit_mode :audit_only +EOM + end + + def run_chef_client_and_expect_exit_code(exit_code) + shell_out!("#{chef_client} -c \"#{path_to('config/client.rb')}\" -o 'x::default'", + :cwd => chef_dir, + :returns => [exit_code]) + end + + context "has a cookbook" do + context "with a library" do + context "which cannot be loaded" do + before do + file "cookbooks/x/recipes/default.rb", "" + file "cookbooks/x/libraries/error.rb", "require 'does/not/exist'" + end + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + end + + context "with an audit recipe" do + context "which fails" do + before do + file "cookbooks/x/recipes/default.rb", <<-RECIPE +control_group "control group without top level control" do + it "should fail" do + expect(4 - 4).to eq(1) + end +end +RECIPE + end + + it "exits with AUDIT_MODE_FAILURE, 42" do + setup_client_rb_with_audit_mode + run_chef_client_and_expect_exit_code 42 + end + end + end + + context "with a recipe" do + context "which throws an error" do + before { file "cookbooks/x/recipes/default.rb", "raise 'BOOM'" } + + it "exits with GENERIC_FAILURE, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + + context "with a recipe which calls Chef::Application.fatal with a non-RFC exit code" do + before { file "cookbooks/x/recipes/default.rb", "Chef::Application.fatal!('BOOM', 123)" } + + it "exits with the GENERIC_FAILURE exit code, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + + context "with a recipe which calls Chef::Application.exit with a non-RFC exit code" do + before { file "cookbooks/x/recipes/default.rb", "Chef::Application.exit!('BOOM', 231)" } + + it "exits with the GENERIC_FAILURE exit code, 1" do + setup_client_rb + run_chef_client_and_expect_exit_code 1 + end + end + + context "when a reboot exception is raised (like from the reboot resource)" do + before do + file "cookbooks/x/recipes/default.rb", <<EOM +raise Chef::Exceptions::Reboot.new +EOM + end + + it "exits with REBOOT_SCHEDULED, 35" do + setup_client_rb + run_chef_client_and_expect_exit_code 35 + end + end + + context "when an attempt to reboot fails (like from the reboot resource)" do + before do + file "cookbooks/x/recipes/default.rb", <<EOM +raise Chef::Exceptions::RebootFailed.new +EOM + end + + it "exits with REBOOT_FAILED, 41" do + setup_client_rb + run_chef_client_and_expect_exit_code 41 + end + end + end + end + end +end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 6473666fbf..0af3916134 100644 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -52,7 +52,8 @@ describe Chef::Application::Apply do describe "when recipe is nil" do it "should raise a fatal with the missing filename message" do - expect(Chef::Application).to receive(:fatal!).with("No recipe file was provided", 1) + expect(Chef::Application).to receive(:fatal!).with("No recipe file was provided", + Chef::Exceptions::RecipeNotFound.new) @app.read_recipe_file(nil) end end @@ -61,7 +62,8 @@ describe Chef::Application::Apply do allow(File).to receive(:exist?).with(@recipe_path).and_return(false) end it "should raise a fatal with the file doesn't exist message" do - expect(Chef::Application).to receive(:fatal!).with(/^No file exists at/, 1) + expect(Chef::Application).to receive(:fatal!).with(/^No file exists at/, + Chef::Exceptions::RecipeNotFound.new) @app.read_recipe_file(@recipe_file_name) end end diff --git a/spec/unit/application/exit_code_spec.rb b/spec/unit/application/exit_code_spec.rb new file mode 100644 index 0000000000..73a113e554 --- /dev/null +++ b/spec/unit/application/exit_code_spec.rb @@ -0,0 +1,231 @@ +# +# Author:: Steven Murawski (<smurawski@chef.io>) +# Copyright:: Copyright 2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "chef" +require "spec_helper" + +require "chef/application/exit_code" + +describe Chef::Application::ExitCode do + + let(:exit_codes) { Chef::Application::ExitCode } + + let(:valid_rfc_exit_codes) { Chef::Application::ExitCode::VALID_RFC_062_EXIT_CODES.values } + + context "Validates the return codes from RFC 062" do + + before do + allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:enabled) + end + + it "validates a SUCCESS return code of 0" do + expect(valid_rfc_exit_codes.include?(0)).to eq(true) + end + + it "validates a GENERIC_FAILURE return code of 1" do + expect(valid_rfc_exit_codes.include?(1)).to eq(true) + end + + it "validates a SIGINT_RECEIVED return code of 2" do + expect(valid_rfc_exit_codes.include?(2)).to eq(true) + end + + it "validates a SIGTERM_RECEIVED return code of 3" do + expect(valid_rfc_exit_codes.include?(3)).to eq(true) + end + + it "validates a AUDIT_MODE_FAILURE return code of 42" do + expect(valid_rfc_exit_codes.include?(42)).to eq(true) + end + + it "validates a REBOOT_SCHEDULED return code of 35" do + expect(valid_rfc_exit_codes.include?(35)).to eq(true) + end + + it "validates a REBOOT_NEEDED return code of 37" do + expect(valid_rfc_exit_codes.include?(37)).to eq(true) + end + + it "validates a REBOOT_FAILED return code of 41" do + expect(valid_rfc_exit_codes.include?(41)).to eq(true) + end + end + + context "when Chef::Config :exit_status is not configured" do + before do + allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(nil) + allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false) + end + + it "writes a deprecation warning" do + warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \ + " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \ + " In a future release, non-standard exit codes will be redefined as" \ + " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb." + expect(Chef).to receive(:log_deprecation).with(warn) + expect(exit_codes.normalize_exit_code(151)).to eq(151) + end + + it "does not modify non-RFC exit codes" do + expect(exit_codes.normalize_exit_code(151)).to eq(151) + end + + it "returns DEPRECATED_FAILURE when no exit code is specified" do + expect(exit_codes.normalize_exit_code()).to eq(-1) + end + + it "returns SIGINT_RECEIVED when a SIGINT is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigInt.new("BOOM"))).to eq(2) + end + + it "returns SIGTERM_RECEIVED when a SIGTERM is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigTerm.new("BOOM"))).to eq(3) + end + + it "returns SIGINT_RECEIVED when a deprecated exit code error is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(2) + end + + it "returns GENERIC_FAILURE when an exception is specified" do + expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1) + end + + end + + context "when Chef::Config :exit_status is configured to not validate exit codes" do + before do + allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:disabled) + allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false) + end + + it "does not write a deprecation warning" do + warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \ + " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \ + " In a future release, non-standard exit codes will be redefined as" \ + " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb." + expect(Chef).not_to receive(:log_deprecation).with(warn) + expect(exit_codes.normalize_exit_code(151)).to eq(151) + end + + it "does not modify non-RFC exit codes" do + expect(exit_codes.normalize_exit_code(151)).to eq(151) + end + + it "returns DEPRECATED_FAILURE when no exit code is specified" do + expect(exit_codes.normalize_exit_code()).to eq(-1) + end + + it "returns GENERIC_FAILURE when an exception is specified" do + expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1) + end + + it "returns SUCCESS when a reboot is pending" do + allow(Chef::DSL::RebootPending).to receive(:reboot_pending?).and_return(true) + expect(exit_codes.normalize_exit_code(0)).to eq(0) + end + + it "returns SIGINT_RECEIVED when a SIGINT is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigInt.new("BOOM"))).to eq(2) + end + + it "returns SIGTERM_RECEIVED when a SIGTERM is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigTerm.new("BOOM"))).to eq(3) + end + + it "returns SIGINT_RECEIVED when a deprecated exit code error is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(2) + end + end + + context "when Chef::Config :exit_status is configured to validate exit codes" do + before do + allow(Chef::Config).to receive(:[]).with(:exit_status).and_return(:enabled) + allow(Chef::Config).to receive(:[]).with(:treat_deprecation_warnings_as_errors).and_return(false) + end + + it "does write a deprecation warning" do + warn = "Chef RFC 062 (https://github.com/chef/chef-rfc/master/rfc062-exit-status.md) defines the" \ + " exit codes that should be used with Chef. Chef::Application::ExitCode defines valid exit codes" \ + " In a future release, non-standard exit codes will be redefined as" \ + " GENERIC_FAILURE unless `exit_status` is set to `:disabled` in your client.rb." + expect(Chef).to receive(:log_deprecation).with(warn) + expect(exit_codes.normalize_exit_code(151)).to eq(1) + end + + it "returns a GENERIC_FAILURE for non-RFC exit codes" do + expect(exit_codes.normalize_exit_code(151)).to eq(1) + end + + it "returns GENERIC_FAILURE when no exit code is specified" do + expect(exit_codes.normalize_exit_code()).to eq(1) + end + + it "returns SIGINT_RECEIVED when a SIGINT is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigInt.new("BOOM"))).to eq(2) + end + + it "returns SIGTERM_RECEIVED when a SIGTERM is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::SigTerm.new("BOOM"))).to eq(3) + end + + it "returns GENERIC_FAILURE when a deprecated exit code error is received" do + expect(exit_codes.normalize_exit_code(Chef::Exceptions::DeprecatedExitCode.new("BOOM"))).to eq(1) + end + + it "returns GENERIC_FAILURE when an exception is specified" do + expect(exit_codes.normalize_exit_code(Exception.new("BOOM"))).to eq(1) + end + + it "returns AUDIT_MODE_FAILURE when there is an audit error" do + audit_error = Chef::Exceptions::AuditError.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(audit_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(42) + end + + it "returns REBOOT_SCHEDULED when there is an reboot requested" do + reboot_error = Chef::Exceptions::Reboot.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(35) + end + + it "returns REBOOT_FAILED when the reboot command fails" do + reboot_error = Chef::Exceptions::RebootFailed.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(41) + end + + it "returns REBOOT_NEEDED when a reboot is pending" do + reboot_error = Chef::Exceptions::RebootPending.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(reboot_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(37) + end + + it "returns SIGINT_RECEIVED when a SIGINT is received." do + sigint_error = Chef::Exceptions::SigInt.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(sigint_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(2) + end + + it "returns SIGTERM_RECEIVED when a SIGTERM is received." do + sigterm_error = Chef::Exceptions::SigTerm.new("BOOM") + runtime_error = Chef::Exceptions::RunFailedWrappingError.new(sigterm_error) + expect(exit_codes.normalize_exit_code(runtime_error)).to eq(3) + end + end + +end diff --git a/spec/unit/config_fetcher_spec.rb b/spec/unit/config_fetcher_spec.rb index 35cf27f2af..6847ee5fd3 100644 --- a/spec/unit/config_fetcher_spec.rb +++ b/spec/unit/config_fetcher_spec.rb @@ -58,7 +58,7 @@ describe Chef::ConfigFetcher do and_return(invalid_json) expect(Chef::Application).to receive(:fatal!). - with(invalid_json_error_regex, 2) + with(invalid_json_error_regex, Chef::Exceptions::DeprecatedExitCode.new) fetcher.fetch_json end end @@ -104,7 +104,7 @@ describe Chef::ConfigFetcher do with("").and_return(invalid_json) expect(Chef::Application).to receive(:fatal!). - with(invalid_json_error_regex, 2) + with(invalid_json_error_regex, Chef::Exceptions::DeprecatedExitCode.new) fetcher.fetch_json end end diff --git a/spec/unit/provider/package/chocolatey_spec.rb b/spec/unit/provider/package/chocolatey_spec.rb index 8eaa69b598..8344c3d0ec 100644 --- a/spec/unit/provider/package/chocolatey_spec.rb +++ b/spec/unit/provider/package/chocolatey_spec.rb @@ -59,7 +59,7 @@ Git|2.6.2 munin-node|1.6.1.20130823 EOF remote_list_obj = double(stdout: remote_list_stdout) - allow(provider).to receive(:shell_out!).with("#{choco_exe} list -ar #{package_names.join ' '}#{args}", { timeout: timeout }).and_return(remote_list_obj) + allow(provider).to receive(:shell_out!).with("#{choco_exe} list -r #{package_names.join ' '}#{args}", { timeout: timeout }).and_return(remote_list_obj) end describe "#initialize" do @@ -84,12 +84,6 @@ munin-node|1.6.1.20130823 expect(provider.candidate_version).to eql(["2.6.1"]) end - it "should set the candidate_version to nill if pinning to bogus version" do - allow_remote_list(["git"]) - new_resource.version("2.5.0") - expect(provider.candidate_version).to eql([nil]) - end - it "should set the candidate_version to nil if there is no candidate" do allow_remote_list(["vim"]) new_resource.package_name("vim") @@ -301,14 +295,6 @@ munin-node|1.6.1.20130823 expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) end - it "installing a package version that does not exist throws an error" do - allow_remote_list(["git"]) - new_resource.package_name("git") - new_resource.version("2.7.0") - provider.load_current_resource - expect { provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package) - end - it "installing multiple packages with a package that does not exist throws an error" do allow_remote_list(["git", "package-does-not-exist"]) new_resource.package_name(["git", "package-does-not-exist"]) diff --git a/spec/unit/provider/package/portage_spec.rb b/spec/unit/provider/package/portage_spec.rb index c2ff1cc952..ebb5b3139f 100644 --- a/spec/unit/provider/package/portage_spec.rb +++ b/spec/unit/provider/package/portage_spec.rb @@ -56,6 +56,12 @@ describe Chef::Provider::Package::Portage, "load_current_resource" do expect(@provider.current_resource.version).to eq("1.0.0-r1") end + it "should return a current resource with the correct version if the package is found with version with character" do + allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/git-1.0.0d"]) + @provider.load_current_resource + expect(@provider.current_resource.version).to eq("1.0.0d") + end + it "should return a current resource with a nil version if the package is not found" do allow(::Dir).to receive(:[]).with("/var/db/pkg/dev-util/git-*").and_return(["/var/db/pkg/dev-util/notgit-1.0.0"]) @provider.load_current_resource diff --git a/spec/unit/provider/package/yum/yum_cache_spec.rb b/spec/unit/provider/package/yum/yum_cache_spec.rb new file mode 100644 index 0000000000..e9d615d734 --- /dev/null +++ b/spec/unit/provider/package/yum/yum_cache_spec.rb @@ -0,0 +1,27 @@ +# +# Author:: Adam Jacob (<adam@chef.io>) +# Copyright:: Copyright 2008-2016, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Chef::Provider::Package::Yum::YumCache do + + it "can find yum-dump.py" do + expect(File.exist?(Chef::Provider::Package::Yum::YumCache.instance.yum_dump_path)).to be true + end + +end diff --git a/spec/unit/provider/systemd_unit_spec.rb b/spec/unit/provider/systemd_unit_spec.rb index 0babc30808..42604c22eb 100644 --- a/spec/unit/provider/systemd_unit_spec.rb +++ b/spec/unit/provider/systemd_unit_spec.rb @@ -654,6 +654,69 @@ describe Chef::Provider::SystemdUnit do end end + describe "try-restarts the unit" do + context "when a user is specified" do + it "try-restarts the unit" do + current_resource.user(user_name) + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --user try-restart #{unit_name}", user_cmd_opts) + .and_return(shell_out_success) + provider.action_try_restart + end + end + + context "when no user is specified" do + it "try-restarts the unit" do + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --system try-restart #{unit_name}", {}) + .and_return(shell_out_success) + provider.action_try_restart + end + end + end + + describe "reload-or-restarts the unit" do + context "when a user is specified" do + it "reload-or-restarts the unit" do + current_resource.user(user_name) + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --user reload-or-restart #{unit_name}", user_cmd_opts) + .and_return(shell_out_success) + provider.action_reload_or_restart + end + end + + context "when no user is specified" do + it "reload-or-restarts the unit" do + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --system reload-or-restart #{unit_name}", {}) + .and_return(shell_out_success) + provider.action_reload_or_restart + end + end + end + + describe "reload-or-try-restarts the unit" do + context "when a user is specified" do + it "reload-or-try-restarts the unit" do + current_resource.user(user_name) + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --user reload-or-try-restart #{unit_name}", user_cmd_opts) + .and_return(shell_out_success) + provider.action_reload_or_try_restart + end + end + + context "when no user is specified" do + it "reload-or-try-restarts the unit" do + expect(provider).to receive(:shell_out_with_systems_locale!) + .with("#{systemctl_path} --system reload-or-try-restart #{unit_name}", {}) + .and_return(shell_out_success) + provider.action_reload_or_try_restart + end + end + end + describe "#active?" do before(:each) do provider.current_resource = current_resource diff --git a/tasks/bin/bundle-platform b/tasks/bin/bundle-platform index 7c77393cb1..4bd659d307 100755 --- a/tasks/bin/bundle-platform +++ b/tasks/bin/bundle-platform @@ -1,10 +1,14 @@ #!/usr/bin/env ruby platforms = ARGV.shift +platforms = platforms.split(" ").map { |p| Gem::Platform.new(p) } +Gem::Platform.instance_eval { @local = platforms.last } old_platforms = Gem.platforms -Gem.platforms = platforms.split(" ").map { |p| Gem::Platform.new(p) } +Gem.platforms = platforms puts "bundle-platform set Gem.platforms to #{Gem.platforms.map { |p| p.to_s }} (was #{old_platforms.map { |p| p.to_s } })" +desired_version = ARGV.shift.delete("_") + # The rest of this is a normal bundler binstub require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", @@ -12,4 +16,4 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", require "rubygems" -load Gem.bin_path("bundler", "bundle") +load Gem.bin_path("bundler", "bundle", desired_version) diff --git a/tasks/bundle_util.rb b/tasks/bundle_util.rb index a057db858a..67647dd4f0 100644 --- a/tasks/bundle_util.rb +++ b/tasks/bundle_util.rb @@ -57,7 +57,14 @@ module BundleUtil # Run the bundle command ruby_platforms = platform ? PLATFORMS[platform].join(" ") : "ruby" - cmd = Shellwords.join([Gem.ruby, "-S", bundle_platform, ruby_platforms, *args]) + cmd = Shellwords.join([ + Gem.ruby, + "-S", + bundle_platform, + ruby_platforms, + "_#{desired_bundler_version}_", + *args, + ]) puts "#{prefix}#{Shellwords.join(["bundle", *args])}#{platform ? " for #{platform} platform" : ""}:" with_gemfile(gemfile) do puts "#{prefix}BUNDLE_GEMFILE=#{gemfile}" @@ -65,7 +72,7 @@ module BundleUtil if extract_output `#{cmd}` else - unless system(bundle_platform, ruby_platforms, *args) + unless system(bundle_platform, ruby_platforms, "_#{desired_bundler_version}_", *args) raise "#{bundle_platform} failed: exit code #{$?}" end end @@ -91,4 +98,13 @@ module BundleUtil def platforms PLATFORMS.keys end + + def desired_bundler_version + @desired_bundler_version ||= begin + omnibus_overrides = File.join(project_root, "omnibus_overrides.rb") + File.readlines(omnibus_overrides).each do |line| + return $1 if line =~ /^override :bundler, version: "(.+)"$/ + end + end + end end diff --git a/vendor/bundle/bundler/gems/bundler-audit-4e32fca89d75 b/vendor/bundle/bundler/gems/bundler-audit-4e32fca89d75 new file mode 160000 +Subproject 4e32fca89d75f0e249671431ff38aadc02bfb28 diff --git a/vendor/bundle/bundler/gems/chefstyle-52a0d55a9e8f b/vendor/bundle/bundler/gems/chefstyle-52a0d55a9e8f new file mode 160000 +Subproject 52a0d55a9e8fb30d067c0a66371db4ae781a026 |