From b77886a5cd9ac11d04e44a9c557f83de565e32b6 Mon Sep 17 00:00:00 2001 From: Dan Webb Date: Tue, 12 May 2026 09:51:26 +0100 Subject: [PATCH] feat!: migrate packagecloud to custom resources --- .github/workflows/ci.yml | 16 ++- .github/workflows/prevent-file-change.yml | 2 + .github/workflows/release.yml | 2 + Berksfile | 4 +- LIMITATIONS.md | 53 ++++++++ README.md | 5 + attributes/default.rb | 5 - documentation/packagecloud_repo.md | 91 ++++++++------ kitchen.dokken.yml | 57 ++------- kitchen.exec.yml | 7 -- kitchen.global.yml | 11 +- kitchen.yml | 31 +++-- libraries/helper.rb | 14 ++- metadata.rb | 19 +-- migration.md | 35 ++++++ resources/repo.rb | 96 +++++++++++++- spec/spec_helper.rb | 10 ++ spec/unit/resources/repo_spec.rb | 119 ++++++++++++++++++ .../{fixtures => }/cookbooks/test/metadata.rb | 2 + test/cookbooks/test/recipes/default.rb | 24 ++++ test/cookbooks/test/recipes/distro_deps.rb | 5 + .../cookbooks/test/recipes/default.rb | 32 ----- .../cookbooks/test/recipes/distro_deps.rb | 6 - .../default/controls/default_spec.rb | 40 ++++-- test/integration/default/inspec.yml | 11 +- test/integration/default/jakedotrb_spec.rb | 8 -- 26 files changed, 495 insertions(+), 210 deletions(-) create mode 100644 LIMITATIONS.md delete mode 100644 attributes/default.rb delete mode 100644 kitchen.exec.yml create mode 100644 migration.md create mode 100644 spec/spec_helper.rb create mode 100644 spec/unit/resources/repo_spec.rb rename test/{fixtures => }/cookbooks/test/metadata.rb (68%) create mode 100644 test/cookbooks/test/recipes/default.rb create mode 100644 test/cookbooks/test/recipes/distro_deps.rb delete mode 100644 test/fixtures/cookbooks/test/recipes/default.rb delete mode 100644 test/fixtures/cookbooks/test/recipes/distro_deps.rb delete mode 100644 test/integration/default/jakedotrb_spec.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc190ed..e4f3124 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,9 @@ jobs: lint-unit: uses: sous-chefs/.github/.github/workflows/lint-unit.yml@6.0.0 permissions: - actions: write checks: write pull-requests: write statuses: write - issues: write integration: needs: lint-unit @@ -25,16 +23,16 @@ jobs: os: - "almalinux-8" - "almalinux-9" - - "centos-7" - - "centos-stream-8" + - "amazonlinux-2023" - "centos-stream-9" - - "debian-10" - - "debian-11" + - "debian-12" + - "fedora-latest" + - "oraclelinux-8" + - "oraclelinux-9" - "rockylinux-8" - "rockylinux-9" - - "ubuntu-1804" - - "ubuntu-2004" - "ubuntu-2204" + - "ubuntu-2404" suite: - "default" fail-fast: false @@ -43,7 +41,7 @@ jobs: - name: Check out code uses: actions/checkout@v6 - name: Install Chef - uses: actionshub/chef-install@6.0.0 + uses: sous-chefs/.github/.github/actions/install-workstation@6.0.0 - name: Dokken uses: actionshub/test-kitchen@3.0.0 env: diff --git a/.github/workflows/prevent-file-change.yml b/.github/workflows/prevent-file-change.yml index 80d64d7..5d180fa 100644 --- a/.github/workflows/prevent-file-change.yml +++ b/.github/workflows/prevent-file-change.yml @@ -12,5 +12,7 @@ name: prevent-file-change jobs: prevent-file-change: uses: sous-chefs/.github/.github/workflows/prevent-file-change.yml@6.0.0 + permissions: + pull-requests: write secrets: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fcd769a..e892f26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,3 +21,5 @@ jobs: token: ${{ secrets.PORTER_GITHUB_TOKEN }} supermarket_user: ${{ secrets.CHEF_SUPERMARKET_USER }} supermarket_key: ${{ secrets.CHEF_SUPERMARKET_KEY }} + slack_bot_token: ${{ secrets.SLACK_BOT_TOKEN }} + slack_channel_id: ${{ secrets.SLACK_CHANNEL_ID }} diff --git a/Berksfile b/Berksfile index e09849c..4c37302 100644 --- a/Berksfile +++ b/Berksfile @@ -1,7 +1,9 @@ +# frozen_string_literal: true + source 'https://supermarket.chef.io' metadata group :integration do - cookbook 'test', path: 'test/fixtures/cookbooks/test' + cookbook 'test', path: 'test/cookbooks/test' end diff --git a/LIMITATIONS.md b/LIMITATIONS.md new file mode 100644 index 0000000..499b081 --- /dev/null +++ b/LIMITATIONS.md @@ -0,0 +1,53 @@ +# Limitations + +## Package Availability + +This cookbook configures packagecloud.io repositories. It does not install +packagecloud.io itself and does not build packages. + +packagecloud.io documents repository support for RPM, DEB, Debian source, +RubyGems, Python, Node.js, Alpine, Maven, Helm, and generic files. The +`packagecloud_repo` resource manages only APT/DEB, YUM/DNF/RPM, and RubyGems +repository sources. + +### APT (Debian/Ubuntu) + +* packagecloud supports DEB repositories, including `any/any` repositories for + packages that work across Debian-based distributions. +* This cookbook tests Debian 12 and Ubuntu 22.04/24.04. +* EOL Debian and Ubuntu releases were removed from the supported platform and + Kitchen matrices. + +### DNF/YUM (RHEL family) + +* packagecloud supports RPM repositories, including `rpm_any/rpm_any` + repositories for packages that work across RPM-based distributions. +* This cookbook tests AlmaLinux 8/9, Amazon Linux 2023, CentOS Stream 9, + Fedora latest, Oracle Linux 8/9, and Rocky Linux 8/9. +* EOL CentOS Linux 7, CentOS Stream 8, Oracle Linux 7, and Scientific Linux were + removed from the supported platform and Kitchen matrices. + +### Zypper (SUSE) + +* The existing resource does not implement SUSE/Zypper repository management. + openSUSE Leap was removed from Kitchen because the resource type default only + maps Debian, RHEL, Fedora, and Amazon platform families. + +## Architecture Limitations + +Architecture availability is determined by the packages uploaded to each +packagecloud repository. The cookbook writes repository configuration and does +not constrain package architectures. + +## Source/Compiled Installation + +No source or compiled installation path is managed by this cookbook. + +## Known Issues + +* Private RubyGems repository removal can only remove sources that can be + matched from the configured packagecloud repository URL; packagecloud read + tokens may differ between runs. +* APT repository installation still uses the legacy `apt-key` command because + that is the behavior exposed by the existing resource and packagecloud + repository install flow. diff --git a/README.md b/README.md index ad62e74..29722cd 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,11 @@ This cookbook provides a resource for installing repositories. +## Migration + +This cookbook no longer provides node attributes. See [migration.md](migration.md) +for the breaking changes and resource property replacements. + ## Usage ## Resources diff --git a/attributes/default.rb b/attributes/default.rb deleted file mode 100644 index dd733db..0000000 --- a/attributes/default.rb +++ /dev/null @@ -1,5 +0,0 @@ -default['packagecloud']['base_repo_path'] = '/install/repositories/' -default['packagecloud']['gpg_key_path'] = '/gpgkey' -default['packagecloud']['hostname_override'] = nil -default['packagecloud']['proxy_host'] = nil -default['packagecloud']['proxy_port'] = nil diff --git a/documentation/packagecloud_repo.md b/documentation/packagecloud_repo.md index 043d048..9b3b188 100644 --- a/documentation/packagecloud_repo.md +++ b/documentation/packagecloud_repo.md @@ -1,70 +1,79 @@ -# `pacakgecloud_repo` - -The packagecloud_repo resource manages the installation of package repositories on various systems, including Debian, RHEL, Fedora, and Amazon Linux. It supports deb, rpm, and gem package types. -Properties - -The following table provides an overview of the available properties for packagecloud_repo: - -| Property | Type | Description | Default | -|-----------------|---------|------------------------------------------------------------------------------------------------|---------------------------| -| repository | String | The name of the repository to install. | | -| master_token | String | The master token for the repository. This is only required for private repositories. | | -| force_os | String | The OS to force for the repository. This is only required for some repositories. | | -| force_dist | String | The distribution to force for the repository. This is only required for some repositories. | | -| type | String | The type of repository to install. Valid values are `deb`, `rpm`, and `gem`. | | -| base_url | String | The base URL for the repository. This is only required for packagecloud:enterprise users. | `https://packagecloud.io` | -| priority | Integer | The priority of the repository. This is only required for Debian-based systems. | false | -| metadata_expire | String | The metadata expiration time for the repository. This is only required for RHEL-based systems. | 300 | +# packagecloud_repo + +Manages packagecloud.io APT, YUM/DNF, and RubyGems repository sources. + +## Actions + +| Action | Description | +|-----------|-----------------------------------------------| +| `:add` | Adds the repository source. Default action. | +| `:remove` | Removes the repository source where possible. | + +## Properties + +| Property | Type | Default | Description | +|---------------------|------------------------------|---------------------------|------------------------------------------------------------------| +| `repository` | String | name property | packagecloud repository path, such as `owner/repository`. | +| `master_token` | String | `nil` | Master token for private repositories. | +| `force_os` | String | `nil` | Override the detected packagecloud OS name. | +| `force_dist` | String | `nil` | Override the detected packagecloud distribution name. | +| `type` | String | platform family default | Repository type: `deb`, `rpm`, or `gem`. | +| `base_url` | String | `https://packagecloud.io` | Base URL for packagecloud Enterprise installs. | +| `base_repo_path` | String | `/install/repositories/` | packagecloud install API path. | +| `gpg_key_path` | String | `/gpgkey` | packagecloud GPG key path retained for wrapper compatibility. | +| `hostname_override` | String, nil | `nil` | Hostname sent to packagecloud when Ohai cannot determine one. | +| `proxy_host` | String, nil | `nil` | Proxy host used for packagecloud API requests. | +| `proxy_port` | String, Integer, nil | `nil` | Proxy port used for packagecloud API requests. | +| `priority` | Integer, true, false | `false` | Optional YUM repository priority. | +| `metadata_expire` | String | `300` | YUM metadata expiration value. | ## Examples +### Public repository + ```ruby -packagecloud_repo "computology/packagecloud-cookbook-test" do - type "deb" -end +packagecloud_repo 'computology/packagecloud-cookbook-test-public' ``` -### Public Repository +### Private repository ```ruby -packagecloud_repo "computology/packagecloud-cookbook-test-public" +packagecloud_repo 'computology/packagecloud-cookbook-test-private' do + master_token '762748f7ae0bfdb086dd539575bdc8cffdca78c6a9af0db9' +end ``` -### Private Repositories - -For private repositories, you need to supply a `master_token`: +### packagecloud Enterprise ```ruby -packagecloud_repo "computology/packagecloud-cookbook-test-private" do - master_token "762748f7ae0bfdb086dd539575bdc8cffdca78c6a9af0db9" +packagecloud_repo 'computology/packagecloud-cookbook-test-private' do + base_url 'https://packages.example.com' + master_token '762748f7ae0bfdb086dd539575bdc8cffdca78c6a9af0db9' end ``` -### Enterprise Users - -For packagecloud:enterprise users, add `base_url` to your resource: +### Force OS and distribution ```ruby -packagecloud_repo "computology/packagecloud-cookbook-test-private" do - base_url "https://packages.example.com" - master_token "762748f7ae0bfdb086dd539575bdc8cffdca78c6a9af0db9" +packagecloud_repo 'computology/packagecloud-cookbook-test-public' do + force_os 'rhel' + force_dist '9' end ``` -### Force OS and Dist - -For forcing the os and dist for repository install: +### Proxy packagecloud API requests ```ruby packagecloud_repo 'computology/packagecloud-cookbook-test-public' do - force_os 'rhel' - force_dist '6.5' + proxy_host 'myproxy.organization.com' + proxy_port '80' end ``` -This cookbook performs checks to determine if a package exists before attempting to install it. To enable proxy support _for these checks_ (not to be confused with proxy support for your package manager of choice), add the following attributes to your cookbook: +### Remove a repository ```ruby -default['packagecloud']['proxy_host'] = 'myproxy.organization.com' -default['packagecloud']['proxy_port'] = '80' +packagecloud_repo 'computology/packagecloud-cookbook-test-public' do + action :remove +end ``` diff --git a/kitchen.dokken.yml b/kitchen.dokken.yml index 47eff95..672960a 100644 --- a/kitchen.dokken.yml +++ b/kitchen.dokken.yml @@ -1,10 +1,14 @@ +--- driver: name: dokken privileged: true chef_version: <%= ENV['CHEF_VERSION'] || 'current' %> -transport: { name: dokken } -provisioner: { name: dokken } +transport: + name: dokken + +provisioner: + name: dokken platforms: - name: almalinux-8 @@ -22,36 +26,11 @@ platforms: image: dokken/amazonlinux-2023 pid_one_command: /usr/lib/systemd/systemd - - name: centos-7 - driver: - image: dokken/centos-7 - pid_one_command: /usr/lib/systemd/systemd - - - name: centos-stream-8 - driver: - image: dokken/centos-stream-8 - pid_one_command: /usr/lib/systemd/systemd - - name: centos-stream-9 driver: image: dokken/centos-stream-9 pid_one_command: /usr/lib/systemd/systemd - - name: debian-9 - driver: - image: dokken/debian-9 - pid_one_command: /bin/systemd - - - name: debian-10 - driver: - image: dokken/debian-10 - pid_one_command: /bin/systemd - - - name: debian-11 - driver: - image: dokken/debian-11 - pid_one_command: /bin/systemd - - name: debian-12 driver: image: dokken/debian-12 @@ -62,16 +41,6 @@ platforms: image: dokken/fedora-latest pid_one_command: /usr/lib/systemd/systemd - - name: opensuse-leap-15 - driver: - image: dokken/opensuse-leap-15 - pid_one_command: /usr/lib/systemd/systemd - - - name: oraclelinux-7 - driver: - image: dokken/oraclelinux-7 - pid_one_command: /usr/lib/systemd/systemd - - name: oraclelinux-8 driver: image: dokken/oraclelinux-8 @@ -92,22 +61,12 @@ platforms: image: dokken/rockylinux-9 pid_one_command: /usr/lib/systemd/systemd - - name: ubuntu-18.04 - driver: - image: dokken/ubuntu-18.04 - pid_one_command: /bin/systemd - - - name: ubuntu-20.04 - driver: - image: dokken/ubuntu-20.04 - pid_one_command: /bin/systemd - - name: ubuntu-22.04 driver: image: dokken/ubuntu-22.04 pid_one_command: /bin/systemd - - name: ubuntu-23.04 + - name: ubuntu-24.04 driver: - image: dokken/ubuntu-23.04 + image: dokken/ubuntu-24.04 pid_one_command: /bin/systemd diff --git a/kitchen.exec.yml b/kitchen.exec.yml deleted file mode 100644 index ba7b2a9..0000000 --- a/kitchen.exec.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -driver: { name: exec } -transport: { name: exec } - -platforms: - - name: macos-latest - - name: windows-latest diff --git a/kitchen.global.yml b/kitchen.global.yml index a382fcd..0bab8e1 100644 --- a/kitchen.global.yml +++ b/kitchen.global.yml @@ -18,21 +18,12 @@ platforms: - name: almalinux-8 - name: almalinux-9 - name: amazonlinux-2023 - - name: centos-7 - - name: centos-stream-8 - name: centos-stream-9 - - name: debian-9 - - name: debian-10 - - name: debian-11 - name: debian-12 - name: fedora-latest - - name: opensuse-leap-15 - - name: oraclelinux-7 - name: oraclelinux-8 - name: oraclelinux-9 - name: rockylinux-8 - name: rockylinux-9 - - name: ubuntu-18.04 - - name: ubuntu-20.04 - name: ubuntu-22.04 - - name: ubuntu-23.04 + - name: ubuntu-24.04 diff --git a/kitchen.yml b/kitchen.yml index 83d74d6..ccc772a 100644 --- a/kitchen.yml +++ b/kitchen.yml @@ -17,19 +17,28 @@ verifier: platforms: - name: almalinux-8 - - name: amazonlinux-2 - - name: debian-10 - - name: debian-11 - - name: centos-7 - - name: centos-stream-8 + - name: almalinux-9 + - name: amazonlinux-2023 + - name: centos-stream-9 + - name: debian-12 - name: fedora-latest - - name: ubuntu-18.04 - - name: ubuntu-20.04 - - name: ubuntu-22.04 + - name: oraclelinux-8 + - name: oraclelinux-9 - name: rockylinux-8 - - name: opensuse-leap-15 + - name: rockylinux-9 + - name: ubuntu-22.04 + - name: ubuntu-24.04 + +x-run_lists: + default: &default_run_list + - recipe[test::default] + +x-verifiers: + default: &default_verifier + inspec_tests: + - path: test/integration/default suites: - name: default - run_list: - - recipe[test::default] + run_list: *default_run_list + verifier: *default_verifier diff --git a/libraries/helper.rb b/libraries/helper.rb index 584130d..53a9695 100644 --- a/libraries/helper.rb +++ b/libraries/helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module PackageCloud module Helper require 'net/https' @@ -8,8 +10,7 @@ def get(uri, params) req.basic_auth uri.user, uri.password if uri.user - proxy = node['packagecloud'].values_at('proxy_host', 'proxy_port') - http = Net::HTTP.new(uri.hostname, uri.port, *(proxy if proxy.first)) + http = Net::HTTP.new(uri.hostname, uri.port, *proxy_options) http.use_ssl = true resp = http.start { |h| h.request(req) } @@ -28,8 +29,7 @@ def post(uri, params) req.basic_auth uri.user, uri.password if uri.user - proxy = node['packagecloud'].values_at('proxy_host', 'proxy_port') - http = Net::HTTP.new(uri.hostname, uri.port, *(proxy if proxy.first)) + http = Net::HTTP.new(uri.hostname, uri.port, *proxy_options) http.use_ssl = true resp = http.start { |h| h.request(req) } @@ -41,5 +41,11 @@ def post(uri, params) raise resp.inspect end end + + def proxy_options + return [] unless new_resource.proxy_host + + [new_resource.proxy_host, new_resource.proxy_port] + end end end diff --git a/metadata.rb b/metadata.rb index 19f8229..3f056df 100644 --- a/metadata.rb +++ b/metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + name 'packagecloud' maintainer 'Sous Chefs' maintainer_email 'help@sous-chefs.org' @@ -5,14 +7,15 @@ version '2.0.10' source_url 'https://github.com/sous-chefs/packagecloud' issues_url 'https://github.com/sous-chefs/packagecloud/issues' -description 'Installs/Configures packagecloud.io repositories.' +description 'Provides resources for managing packagecloud.io repositories.' chef_version '>= 15.3' -supports 'amazon' -supports 'centos' -supports 'debian' +supports 'almalinux', '>= 8.0' +supports 'amazon', '>= 2023.0' +supports 'centos_stream', '>= 9.0' +supports 'debian', '>= 12.0' supports 'fedora' -supports 'oracle' -supports 'redhat' -supports 'scientific' -supports 'ubuntu' +supports 'oracle', '>= 8.0' +supports 'redhat', '>= 8.0' +supports 'rocky', '>= 8.0' +supports 'ubuntu', '>= 22.04' diff --git a/migration.md b/migration.md new file mode 100644 index 0000000..a7a7537 --- /dev/null +++ b/migration.md @@ -0,0 +1,35 @@ +# Migration + +## Migrating to resource properties + +This release removes the legacy node attributes under `node['packagecloud']`. +Wrapper cookbooks should configure `packagecloud_repo` properties directly. + +* `node['packagecloud']['base_repo_path']` becomes `base_repo_path`. +* `node['packagecloud']['gpg_key_path']` becomes `gpg_key_path`. +* `node['packagecloud']['hostname_override']` becomes `hostname_override`. +* `node['packagecloud']['proxy_host']` becomes `proxy_host`. +* `node['packagecloud']['proxy_port']` becomes `proxy_port`. + +Before: + +```ruby +node.default['packagecloud']['proxy_host'] = 'myproxy.organization.com' +node.default['packagecloud']['proxy_port'] = '80' + +packagecloud_repo 'computology/packagecloud-cookbook-test-public' +``` + +After: + +```ruby +packagecloud_repo 'computology/packagecloud-cookbook-test-public' do + proxy_host 'myproxy.organization.com' + proxy_port '80' +end +``` + +## Test cookbook examples + +See `test/cookbooks/test/recipes/default.rb` for current resource examples used +by Kitchen. diff --git a/resources/repo.rb b/resources/repo.rb index 708da0d..858303c 100644 --- a/resources/repo.rb +++ b/resources/repo.rb @@ -1,4 +1,8 @@ +# frozen_string_literal: true + +provides :packagecloud_repo unified_mode true +default_action :add property :repository, String, @@ -27,6 +31,26 @@ String, default: 'https://packagecloud.io' +property :base_repo_path, + String, + default: '/install/repositories/' + +property :gpg_key_path, + String, + default: '/gpgkey' + +property :hostname_override, + [String, nil] + +property :proxy_host, + [String, nil], + desired_state: false + +property :proxy_port, + [String, Integer, nil], + default: nil, + desired_state: false + property :priority, [Integer, true, false], default: false @@ -49,13 +73,26 @@ end end +action :remove do + case new_resource.type + when 'deb' + remove_deb + when 'rpm' + remove_rpm + when 'gem' + remove_gem + else + raise "#{new_resource.type} is an unknown package type." + end +end + action_class do include ::PackageCloud::Helper require 'uri' def gpg_url(base_url, repo, format, master_token) - base_install_url = ::File.join(base_url, node['packagecloud']['base_repo_path']) + base_install_url = ::File.join(base_url, new_resource.base_repo_path) ext = (format == :deb) ? 'list' : 'repo' gpg_key_url_endpoint = construct_uri_with_options(base_url: base_install_url, repo: repo, endpoint: "gpg_key_url.#{ext}") unless master_token.nil? @@ -114,9 +151,21 @@ def install_deb end end + def remove_deb + file "/etc/apt/sources.list.d/#{filename}.list" do + action :delete + notifies :update, "apt_update[apt-get-update-#{filename}]", :immediately + end + + apt_update "apt-get-update-#{filename}" do + action :nothing + only_if { ::File.directory?('/var/lib/apt/lists') } + end + end + def install_rpm given_base_url = new_resource.base_url - base_repo_url = ::File.join(given_base_url, node['packagecloud']['base_repo_path']) + base_repo_url = ::File.join(given_base_url, new_resource.base_repo_path) base_url_endpoint = construct_uri_with_options(base_url: base_repo_url, repo: new_resource.repository, endpoint: 'rpm_base_url') if new_resource.master_token @@ -180,6 +229,31 @@ def install_rpm end end + def remove_rpm + file "/etc/yum.repos.d/#{filename}.repo" do + action :delete + notifies :run, "execute[yum-makecache-#{filename}]", :immediately + notifies :run, "ruby_block[yum-cache-reload-#{filename}]", :immediately + end + + execute "yum-makecache-#{filename}" do + command 'yum -q makecache -y' + action :nothing + only_if { ::File.exist?('/usr/bin/yum') || ::File.exist?('/usr/bin/dnf') } + end + + ruby_block "yum-cache-reload-#{filename}" do + block do + if node['platform_version'].to_i >= 8 + Chef::Provider::Package::Dnf::PythonHelper.instance.restart + else + Chef::Provider::Package::Yum::YumCache.instance.reload + end + end + action :nothing + end + end + def install_gem repo_url = construct_uri_with_options(base_url: new_resource.base_url, repo: new_resource.repository) repo_url = read_token(repo_url).to_s @@ -190,12 +264,24 @@ def install_gem end end + def remove_gem + repo_url = construct_uri_with_options(base_url: new_resource.base_url, repo: new_resource.repository).to_s + repo_uri = URI(repo_url) + escaped_repo_path = Regexp.escape(repo_uri.path) + escaped_repo_host = Regexp.escape(repo_uri.host) + + execute "remove packagecloud #{new_resource.name} repo as gem source" do + command "gem sources --list | ruby -ne 'puts $_ if $_ =~ %r{#{escaped_repo_host}.*#{escaped_repo_path}}' | xargs -r -n1 gem sources --remove" + only_if "gem sources --list | ruby -ne 'exit 0 if $_ =~ %r{#{escaped_repo_host}.*#{escaped_repo_path}}; END { exit 1 }'" + end + end + def read_token(repo_url) return repo_url unless new_resource.master_token base_url = new_resource.base_url - base_repo_url = ::File.join(base_url, node['packagecloud']['base_repo_path']) + base_repo_url = ::File.join(base_url, new_resource.base_repo_path) uri = construct_uri_with_options(base_url: base_repo_url, repo: new_resource.repository, endpoint: 'tokens.text') uri.user = new_resource.master_token @@ -213,12 +299,12 @@ def read_token(repo_url) def install_endpoint_params dist = dist_name - hostname = node['packagecloud']['hostname_override'] || + hostname = new_resource.hostname_override || node['fqdn'] || node['hostname'] if !hostname || hostname.empty? - raise("Can't determine hostname! Set node['packagecloud']['hostname_override'] " \ + raise("Can't determine hostname! Set the packagecloud_repo hostname_override property " \ 'if it cannot be automatically determined by Ohai.') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..63e556f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'chefspec' +require 'chefspec/berkshelf' + +RSpec.configure do |config| + config.color = true + config.formatter = :documentation + config.log_level = :error +end diff --git a/spec/unit/resources/repo_spec.rb b/spec/unit/resources/repo_spec.rb new file mode 100644 index 0000000..f9ffff0 --- /dev/null +++ b/spec/unit/resources/repo_spec.rb @@ -0,0 +1,119 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'packagecloud_repo' do + step_into :packagecloud_repo + + let(:http) { instance_double(Net::HTTP) } + let(:gpg_response) do + Net::HTTPOK.new('1.1', '200', 'OK').tap do |response| + allow(response).to receive(:body).and_return("https://packagecloud.io/test/repo/gpgkey\n") + end + end + let(:rpm_base_response) do + Net::HTTPOK.new('1.1', '200', 'OK').tap do |response| + allow(response).to receive(:body).and_return("https://packagecloud.io/test/repo/el/9/$basearch\n") + end + end + + before do + allow(Net::HTTP).to receive(:new).and_return(http) + allow(http).to receive(:use_ssl=) + allow(http).to receive(:start).and_yield(http) + allow(http).to receive(:request).and_return(gpg_response) + end + + context 'on ubuntu' do + platform 'ubuntu', '24.04' + + context 'with default properties' do + recipe do + packagecloud_repo 'test/repo' do + force_os 'any' + force_dist 'any' + end + end + + it { is_expected.to install_package('wget') } + it { is_expected.to install_package('apt-transport-https') } + it { is_expected.to install_package('lsb-release') } + it { is_expected.to create_template('/etc/apt/sources.list.d/test_repo.list') } + it { is_expected.to_not run_execute('apt-key-add-test_repo') } + it { is_expected.to_not run_execute('apt-get-update-test_repo') } + end + + context 'with action :remove' do + recipe do + packagecloud_repo 'test/repo' do + type 'deb' + action :remove + end + end + + it { is_expected.to delete_file('/etc/apt/sources.list.d/test_repo.list') } + it { is_expected.to_not update_apt_update('apt-get-update-test_repo') } + end + end + + context 'on almalinux' do + platform 'almalinux', '9' + + before do + allow(http).to receive(:request).and_return(rpm_base_response, gpg_response) + end + + context 'with rpm defaults' do + recipe do + packagecloud_repo 'test/repo' + end + + it { is_expected.to create_template('/etc/yum.repos.d/test_repo.repo') } + it { is_expected.to_not run_execute('yum-makecache-test_repo') } + it { is_expected.to_not run_ruby_block('yum-cache-reload-test_repo') } + end + + context 'with action :remove' do + recipe do + packagecloud_repo 'test/repo' do + type 'rpm' + action :remove + end + end + + it { is_expected.to delete_file('/etc/yum.repos.d/test_repo.repo') } + it { is_expected.to_not run_execute('yum-makecache-test_repo') } + it { is_expected.to_not run_ruby_block('yum-cache-reload-test_repo') } + end + end + + context 'with gem source' do + platform 'ubuntu', '24.04' + + before do + stub_command('gem source --list | grep https://packagecloud.io/test/repo/').and_return(false) + stub_command("gem sources --list | ruby -ne 'exit 0 if $_ =~ %r{packagecloud\\.io.*/test/repo/}; END { exit 1 }'").and_return(true) + end + + context 'with action :add' do + recipe do + packagecloud_repo 'test/repo' do + type 'gem' + end + end + + it { is_expected.to run_execute('install packagecloud test/repo repo as gem source') } + end + + context 'with action :remove' do + recipe do + packagecloud_repo 'test/repo' do + type 'gem' + action :remove + end + end + + it { is_expected.to run_execute('remove packagecloud test/repo repo as gem source') } + end + end +end diff --git a/test/fixtures/cookbooks/test/metadata.rb b/test/cookbooks/test/metadata.rb similarity index 68% rename from test/fixtures/cookbooks/test/metadata.rb rename to test/cookbooks/test/metadata.rb index 59967ff..dba0a79 100644 --- a/test/fixtures/cookbooks/test/metadata.rb +++ b/test/cookbooks/test/metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + name 'test' depends 'yum' diff --git a/test/cookbooks/test/recipes/default.rb b/test/cookbooks/test/recipes/default.rb new file mode 100644 index 0000000..5383215 --- /dev/null +++ b/test/cookbooks/test/recipes/default.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +apt_update + +include_recipe 'test::distro_deps' + +packagecloud_repo 'damacus/packagecloud-test' do + if platform_family?('debian') + force_os 'ubuntu' + force_dist 'jammy' + elsif platform?('amazon') + force_os 'el' + force_dist '9' + elsif platform_family?('rhel') + force_os 'el' + force_dist node['platform_version'].to_i.to_s + end +end + +if platform_family?('debian') + packagecloud_repo 'damacus/packagecloud-test' do + type 'gem' + end +end diff --git a/test/cookbooks/test/recipes/distro_deps.rb b/test/cookbooks/test/recipes/distro_deps.rb new file mode 100644 index 0000000..3770235 --- /dev/null +++ b/test/cookbooks/test/recipes/distro_deps.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +if platform_family?('debian') + package %w(ruby dpkg-dev rubygems) +end diff --git a/test/fixtures/cookbooks/test/recipes/default.rb b/test/fixtures/cookbooks/test/recipes/default.rb deleted file mode 100644 index a67d160..0000000 --- a/test/fixtures/cookbooks/test/recipes/default.rb +++ /dev/null @@ -1,32 +0,0 @@ -apt_update - -include_recipe 'test::distro_deps' - -packagecloud_repo 'damacus/packagecloud-test' - -packagecloud_repo 'damacus/packagecloud-test-private' do - master_token '81aa4bd4e1bcc8eac6daf9862c46965c538e0ff74456ddb7' -end - -packagecloud_repo 'damacus/packagecloud-test' do - type 'gem' -end - -packagecloud_repo 'damacus/packagecloud-test-private' do - type 'gem' - master_token '81aa4bd4e1bcc8eac6daf9862c46965c538e0ff74456ddb7' -end - -package 'jake' # Private -package 'packagecloud-test' # Public - -# gem_package 'jakedotrb' do -# options '--bindir /usr/local/bin' -# version '0.0.1' -# source 'https://packagecloud.io/damacus/packagecloud-test-private' -# end - -gem_package 'packagecloud-test-gem' do - source 'https://packagecloud.io/damacus/packagecloud-test' - options '--bindir /opt/bin' -end diff --git a/test/fixtures/cookbooks/test/recipes/distro_deps.rb b/test/fixtures/cookbooks/test/recipes/distro_deps.rb deleted file mode 100644 index 3e6ba90..0000000 --- a/test/fixtures/cookbooks/test/recipes/distro_deps.rb +++ /dev/null @@ -1,6 +0,0 @@ -case node['platform_family'] -when 'rhel', 'fedora', 'amazon' - package %w(ruby rubygems) -when 'debian' - package %w(ruby dpkg-dev rubygems) -end diff --git a/test/integration/default/controls/default_spec.rb b/test/integration/default/controls/default_spec.rb index 8a58902..f53db95 100644 --- a/test/integration/default/controls/default_spec.rb +++ b/test/integration/default/controls/default_spec.rb @@ -1,12 +1,34 @@ -describe command('ls /usr/local/bin/jake') do - its(:exit_status) { should eq 0 } -end +# frozen_string_literal: true -describe command('/usr/local/bin/jake') do - its(:exit_status) { should eq 0 } - its(:stdout) { should eq "as it so happens, jake douglas is a very nice young man.\n" } -end +title 'Default Tests' + +if os.family == 'debian' + control 'packagecloud-apt-01' do + impact 1.0 + title 'APT repository is configured' + + describe file('/etc/apt/sources.list.d/damacus_packagecloud-test.list') do + it { should exist } + its('content') { should match(%r{https://packagecloud.io/damacus/packagecloud-test/ubuntu jammy main}) } + end + end + + control 'packagecloud-gem-01' do + impact 0.7 + title 'RubyGems source is configured' + + describe command('gem sources --list') do + its('stdout') { should match(%r{https://packagecloud.io/damacus/packagecloud-test/}) } + end + end +else + control 'packagecloud-rpm-01' do + impact 1.0 + title 'RPM repository is configured' -describe package('packagecloud-test') do - it { should be_installed } + describe file('/etc/yum.repos.d/damacus_packagecloud-test.repo') do + it { should exist } + its('content') { should match(%r{https://packagecloud.io/damacus/packagecloud-test/(el|fedora)/}) } + end + end end diff --git a/test/integration/default/inspec.yml b/test/integration/default/inspec.yml index 7f2562b..3ebc98a 100644 --- a/test/integration/default/inspec.yml +++ b/test/integration/default/inspec.yml @@ -1,6 +1,7 @@ --- -name: packagecloud-integration-tests -title: Integration tests for packagecloud cookbook -summary: This InSpec profile contains integration tests for packagecloud cookbook -supports: - - os-family: linux +name: default +title: Default Tests +maintainer: Sous Chefs +license: Apache-2.0 +summary: Default integration tests for the packagecloud cookbook +version: 1.0.0 diff --git a/test/integration/default/jakedotrb_spec.rb b/test/integration/default/jakedotrb_spec.rb deleted file mode 100644 index bd0a7de..0000000 --- a/test/integration/default/jakedotrb_spec.rb +++ /dev/null @@ -1,8 +0,0 @@ -describe command('/usr/local/bin/jakedotrb') do - its('exit_status') { should eq 0 } - its('stdout') { should match /jake douglas is a very nice young man./ } -end - -describe command('gem list jakedotrb') do - its('exit_status') { should eq 0 } -end