diff --git a/.kitchen.yml b/.kitchen.yml index 5746511..c638107 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -96,3 +96,21 @@ suites: accesskey: controller-accesskey java_agent: source: http://10.0.72.177:10000/JavaAppAgent.zip + - name: php_agent + run_list: + - recipe[apt::default] + - recipe[php::default] + - recipe[appdynamics::php_agent] + attributes: + appdynamics: + app_name: test-app + tier_name: test-tier + node_name: test-node + controller: + host: controller-host + port: 1234 + ssl: true + user: controller-user + accesskey: controller-accesskey + php_agent: + source: http://10.0.72.177:10000/appdynamics-php-agent.tar.bz2 diff --git a/Gemfile b/Gemfile index 68f2e47..c463030 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gem 'chefspec' gem 'rubocop' gem 'rspec' gem 'foodcritic' +gem 'fauxhai' group :dev do gem 'test-kitchen' diff --git a/Gemfile.lock b/Gemfile.lock index aa8729d..652c48b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -244,6 +244,7 @@ PLATFORMS DEPENDENCIES berkshelf chefspec + fauxhai foodcritic kitchen-vagrant rspec diff --git a/attributes/php_agent.rb b/attributes/php_agent.rb new file mode 100644 index 0000000..2565a2b --- /dev/null +++ b/attributes/php_agent.rb @@ -0,0 +1,5 @@ +default['appdynamics']['php_agent']['source'] = nil +default['appdynamics']['php_agent']['owner'] = nil +default['appdynamics']['php_agent']['group'] = nil +default['appdynamics']['php_agent']['dest'] = '/opt/appdynamics' +default['appdynamics']['php_agent']['checksum'] = nil diff --git a/libraries/helpers.rb b/libraries/helpers.rb new file mode 100644 index 0000000..84225fc --- /dev/null +++ b/libraries/helpers.rb @@ -0,0 +1,33 @@ +module AppDynamicsCookbook + module Helpers + def platform(family, supported) + plat = family + + case family + when 'mac_os_x' + plat = 'osx' + when 'GNU/Linux' + plat = 'linux' + end + + raise "Unsupported OS family #{plat}" if supported and not supported.include? plat + + plat + end + + def architecture(machine, supported) + arch = machine + + case arch + when 'i386' + arch = 'x86' + when 'x86_64' + arch = 'x64' + end + + raise "Unsupported CPU architecture" if supported and not supported.include? arch + + arch + end + end +end diff --git a/libraries/matchers.rb b/libraries/matchers.rb new file mode 100644 index 0000000..9153b50 --- /dev/null +++ b/libraries/matchers.rb @@ -0,0 +1,32 @@ +# +# Author:: Dan Koepke +# Cookbook Name:: appdynamics +# Library:: ChefSpec matchers +# +# Copyright:: 2015, AppDynamics, Inc and its affiliates +# +# 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. +# + +if defined?(ChefSpec) + ChefSpec.define_matcher :appdynamics_extract + ChefSpec.define_matcher :appdynamics_php_agent + + def run_appdynamics_extract(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:appdynamics_extract, :run, resource_name) + end + + def install_appdynamics_php_agent(resource_name) + ChefSpec::Matchers::ResourceMatcher.new(:appdynamics_php_agent, :install, resource_name) + end +end diff --git a/metadata.rb b/metadata.rb index 1115e6f..f22344c 100644 --- a/metadata.rb +++ b/metadata.rb @@ -1,8 +1,9 @@ name 'appdynamics' -version '0.0.0' +version '0.2.0' -depends 'windows' -depends 'python' -depends 'nodejs' -depends 'java' depends 'apt' +depends 'java' +depends 'nodejs' +depends 'php' +depends 'python' +depends 'windows' diff --git a/providers/extract.rb b/providers/extract.rb new file mode 100644 index 0000000..54d7d79 --- /dev/null +++ b/providers/extract.rb @@ -0,0 +1,78 @@ +# +# Author:: Dan Koepke +# Cookbook Name:: appdynamics +# Provider:: extract +# +# Copyright:: 2015, AppDynamics, Inc and its affiliates +# +# 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. +# + +commands = { :zip => ['unzip', '-qq'], :tar_bz2 => ['tar', 'xjf'], :tar_gz => ['tar', 'xzf'] } + +def whyrun_supported? + true +end + +use_inline_resources + +action :run do + tmp_dest = "#{Chef::Config[:file_cache_path]}/#{new_resource.name}" + + install_type = new_resource.type || guess_type(new_resource.basename) + package 'unzip' if install_type == :zip + + raise "Unsupported type #{install_type}" if commands[install_type].nil? + install_command = commands[install_type] + [tmp_dest] + + directory "#{new_resource.name} :create #{new_resource.dest}" do + recursive true + path new_resource.dest + owner new_resource.owner unless new_resource.owner.nil? + group new_resource.group unless new_resource.group.nil? + mode '0644' + end + + remote_file "#{new_resource.name} :create #{tmp_dest}" do + source new_resource.source + path tmp_dest + owner new_resource.owner unless new_resource.owner.nil? + group new_resource.group unless new_resource.group.nil? + checksum new_resource.checksum unless new_resource.checksum.nil? + notifies :run, "execute[#{new_resource.name} :run extract]", :immediately + end + + new_resource.updated_by_last_action(true) + + execute "#{new_resource.name} :run extract" do + action :nothing + + command install_command + cwd new_resource.dest + user new_resource.owner unless new_resource.owner.nil? + group new_resource.group unless new_resource.group.nil? + end + + new_resource.updated_by_last_action(true) +end + +def guess_type(name) + if name.end_with? '.zip' + return :zip + elsif name.end_with? '.tar.bz2' + return :tar_bz2 + elsif name.end_with? '.tar.gz' + return :tar_gz + end + raise "Cannot guess archive type, please specify 'type' attribute" +end diff --git a/providers/php_agent.rb b/providers/php_agent.rb new file mode 100644 index 0000000..52ea434 --- /dev/null +++ b/providers/php_agent.rb @@ -0,0 +1,74 @@ +# +# Author:: Dan Koepke +# Cookbook Name:: appdynamics +# Provider:: php_agent +# +# Copyright:: 2015, AppDynamics, Inc and its affiliates +# +# 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_relative '../libraries/helpers' +include AppDynamicsCookbook::Helpers + +def whyrun_supported? + true +end + +use_inline_resources + +action :install do + version = new_resource.version + tarball_source = new_resource.source || default_source(version) + + appdynamics_extract "#{new_resource.name} :run extract" do + basename "appdynamics-php-agent-#{version}.tar.bz2" + type :tar_bz2 + source tarball_source + dest new_resource.dest + owner new_resource.owner unless new_resource.owner.nil? + group new_resource.group unless new_resource.group.nil? + notifies :run, "execute[#{new_resource.name} :run install.sh]", :immediately + end + + new_resource.updated_by_last_action(true) + + install_command = ["./install.sh"] + install_command << '-s' if new_resource.controller_ssl + install_command << "-a=#{new_resource.account}@#{new_resource.accesskey}" + install_command << "--http-proxy-host=#{new_resoure.http_proxy_host}" if new_resource.http_proxy_host + install_command << "--http-proxy-port=#{new_resoure.http_proxy_port}" if new_resource.http_proxy_port + install_command << "--http-proxy-user=#{new_resoure.http_proxy_user}" if new_resource.http_proxy_user + install_command << "--http-proxy-password-file=#{new_resoure.http_proxy_password_file}" if new_resource.http_proxy_password_file + install_command << new_resource.controller_host + install_command << new_resource.controller_port + install_command << new_resource.app_name + install_comamnd << new_resource.tier_name + install_command << new_resource.node_name + + execute "#{new_resource.name} :run install.sh" do + action :nothing + command install_command + cwd "#{new_resource.dest}/appdynamics-php-agent" + user new_resource.owner unless new_resource.owner.nil? + group new_resource.group unless new_resource.group.nil? + end + + new_resource.updated_by_last_action(true) +end + +def default_source(version) + plat = platform(node['kernel']['os'], %w(linux osx)) + arch = architecture(node['kernel']['machine'], %w(x86 x64)) + "https://packages.appdynamics.com/#{version}/php/appdynamics-php-agent-#{plat}-#{arch}-#{version}.tar.bz2" +end diff --git a/recipes/php_agent.rb b/recipes/php_agent.rb new file mode 100644 index 0000000..e17f22c --- /dev/null +++ b/recipes/php_agent.rb @@ -0,0 +1,24 @@ +appdynamics_php_agent node['appdynamics']['node_name'] do + version node['appdynamics']['php_agent']['version'] + source node['appdynamics']['php_agent']['source'] + dest node['appdynamics']['php_agent']['dest'] + + app_name node['appdynamics']['app_name'] + tier_name node['appdynamics']['tier_name'] + + controller_host node['appdynamics']['controller']['host'] + controller_port node['appdynamics']['controller']['port'] + controller_ssl node['appdynamics']['controller']['ssl'] + account node['appdynamics']['controller']['user'] + accesskey node['appdynamics']['controller']['accesskey'] + + http_proxy_host node['appdynamics']['http_proxy']['host'] + http_proxy_port node['appdynamics']['http_proxy']['port'] + http_proxy_user node['appdynamics']['http_proxy']['user'] + http_proxy_password_file node['appdynamics']['http_proxy']['password_file'] + + dest node['appdynamics']['php_agent']['dest'] + owner node['appdynamics']['php_agent']['owner'] + group node['appdynamics']['php_agent']['group'] + checksum node['appdynamics']['php_agent']['checksum'] +end diff --git a/resources/extract.rb b/resources/extract.rb new file mode 100644 index 0000000..a443708 --- /dev/null +++ b/resources/extract.rb @@ -0,0 +1,31 @@ +# +# Author:: Dan Koepke +# Cookbook Name:: appdynamics +# Resource:: extract +# +# Copyright:: 2015, AppDynamics, Inc and its affiliates +# +# 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. +# + +actions :run +default_action :run + +attribute :basename, kind_of: String, name_attribute: true, required: true +attribute :source, kind_of: String, required: true +attribute :dest, kind_of: String, required: true + +attribute :type, kind_of: [Symbol, NilClass] +attribute :owner, kind_of: [String, Integer, NilClass] +attribute :group, kind_of: [String, Integer, NilClass] +attribute :checksum, kind_of: [String, NilClass] diff --git a/resources/php_agent.rb b/resources/php_agent.rb new file mode 100644 index 0000000..e96af17 --- /dev/null +++ b/resources/php_agent.rb @@ -0,0 +1,25 @@ +actions :install +default_action :install + +attribute :dest, kind_of: String, required: true +attribute :version, kind_of: String, regex: /^\d+\.\d+.\d+.\d+/, required: true +attribute :source, kind_of: [String, NilClass], default: nil +attribute :checksum, kind_of: [String, NilClass], default: nil + +attribute :app_name, kind_of: String, required: true +attribute :tier_name, kind_of: String, required: true +attribute :node_name, kind_of: String, name_attribute: true + +attribute :controller_host, kind_of: String, required: true +attribute :controller_port, kind_of: [String, Integer], default: 443 +attribute :controller_ssl, kind_of: [TrueClass, FalseClass], default: true +attribute :account, kind_of: String, default: 'customer1' +attribute :accesskey, kind_of: String, required: true + +attribute :http_proxy_host, kind_of: [String, NilClass], default: nil +attribute :http_proxy_port, kind_of: [String, Integer, NilClass], default: nil +attribute :http_proxy_user, kind_of: [String, NilClass], default: nil +attribute :http_proxy_password_file, kind_of: [String, NilClass], default: nil + +attribute :owner, kind_of: [String, Integer, NilClass], default: nil +attribute :group, kind_of: [String, Integer, NilClass], default: nil diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index eedb4e9..b5a90b8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,3 +9,5 @@ def fixture_file(*args) Pathname(__FILE__).parent.join('fixtures', *args) end + +at_exit { ChefSpec::Coverage.report! } diff --git a/spec/unit/php_agent_spec.rb b/spec/unit/php_agent_spec.rb new file mode 100644 index 0000000..b624bec --- /dev/null +++ b/spec/unit/php_agent_spec.rb @@ -0,0 +1,61 @@ +require_relative '../spec_helper' +require_relative '../../libraries/helpers' + +describe 'appdynamics::php_agent' do + let(:chef_run) do + ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '12.04', step_into: ['appdynamics_php_agent']) do |node| + node.set['appdynamics'] = { + app_name: 'app-name', + tier_name: 'tier-name', + node_name: 'node-name', + + controller: { + host: 'controller-host', + port: 1234, + account: 'account', + accesskey: 'accesskey' + }, + + php_agent: { + version: '4.1.0.5', + dest: 'dest' + } + } + end.converge(described_recipe) + end + + it 'calls the appdynamics_php_agent resource' do + expect(chef_run).to install_appdynamics_php_agent('node-name').with( + app_name: 'app-name', + tier_name: 'tier-name', + node_name: 'node-name', + version: '4.1.0.5', + dest: 'dest' + ) + end + + it 'downloads and extracts the PHP agent with default source' do + expect(chef_run).to run_appdynamics_extract('node-name :run extract').with( + basename: 'appdynamics-php-agent-4.1.0.5.tar.bz2', + source: 'https://packages.appdynamics.com/4.1.0.5/php/appdynamics-php-agent-linux-x64-4.1.0.5.tar.bz2', + type: :tar_bz2, + dest: 'dest' + ) + end + + it 'notifies to run the install script' do + resource = chef_run.appdynamics_php_agent('node-name').with( + app_name: 'app-name', + tier_name: 'tier-name', + node_name: 'node-name', + version: '4.1.0.5', + dest: 'dest' + ) + expect(resource).to notify('execute[node-name :run install.sh]').to(:run).immediately + end + + it 'runs the install script with the right arguments' do + expect(chef_run).to run_execute('node-name :run install.sh').with( + ) + end +end