From a37d3767d465f3019a8145e3f45f77206a175964 Mon Sep 17 00:00:00 2001 From: Khosrow Moossavi Date: Fri, 4 Jan 2019 13:53:25 -0500 Subject: [PATCH] Overall refactor of Custom Resources (#18) * Add license badge * fix cookstyle * Fix markdown lint and formatting * Convert global_settings to new custom resource format * fix cookstyle * Convert api_keys to new custom resource format * Fix server spec test * Ignore FC075 from Foodcritic * Suppress chefspec warning message * Enhance CircleCI config * Fix kitchen on ci pipeline * Use docker driver for kitchen test * Add kitchen-docker gem * Cosmetic formatting * Remove Vagrant installation * Fix for services inside docker container * Add cloudstack version to usage recipe * Add more platforms to kitchen test * Move test recipes to its standalone folder * Update config for centos7 for now * Show ruby version * Install ruby * Add systemvm upload to test * Convert system_template to new custom resource format * Fix function definition typo * Update CI config * Fix systemvm_template resource and serverspec * Fix cookstyle * Cleanup circle config * Bump version to v6.1.0 --- .circleci/config.yml | 68 ++++-- .foodcritic | 1 + .kitchen.docker.yml | 20 ++ .kitchen.yml | 3 +- Berksfile | 4 + CHANGELOG.md | 148 +++++++------ Gemfile | 1 + README.md | 1 + libraries/api_keys.rb | 180 --------------- libraries/global_setting.rb | 44 ---- libraries/system_template.rb | 58 ----- metadata.rb | 8 +- providers/api_keys.rb | 81 ------- providers/global_setting.rb | 72 ------ providers/system_template.rb | 72 ------ recipes/management_server.rb | 1 - recipes/repo_rhel.rb | 20 +- recipes/repo_ubuntu.rb | 6 +- recipes/usage.rb | 4 +- recipes/vhd-util.rb | 2 +- resources/api_keys.rb | 207 +++++++++++++++++- resources/global_setting.rb | 57 ++++- resources/system_template.rb | 82 ++++++- spec/recipes/default_spec.rb | 25 +-- spec/spec_helper.rb | 24 +- test/cookbooks/test/metadata.rb | 4 + .../cookbooks/test/recipes/all-in-one.rb | 20 +- .../default/serverspec/systemvm_spec.rb | 24 ++ 28 files changed, 540 insertions(+), 697 deletions(-) create mode 100644 .foodcritic create mode 100644 .kitchen.docker.yml delete mode 100644 libraries/api_keys.rb delete mode 100644 libraries/global_setting.rb delete mode 100644 libraries/system_template.rb delete mode 100644 providers/api_keys.rb delete mode 100644 providers/global_setting.rb delete mode 100644 providers/system_template.rb create mode 100644 test/cookbooks/test/metadata.rb rename recipes/circle-ci.rb => test/cookbooks/test/recipes/all-in-one.rb (75%) create mode 100644 test/integration/default/serverspec/systemvm_spec.rb diff --git a/.circleci/config.yml b/.circleci/config.yml index af6dee9..51e58ff 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ defaults: &defaults docker: - - image: chef/chefdk + - image: chef/chefdk:3.6.57 working_directory: ~/cookbook/cookbook_cloudstack version: 2 @@ -19,42 +19,68 @@ jobs: steps: - restore_cache: key: repo-{{ .Environment.CIRCLE_SHA1 }} - - run: chef --version - - run: chef exec cookstyle . + - run: + name: "Chef and Ruby version" + command: | + chef --version + ruby --version + - run: + name: "Check Cookstyle" + command: chef exec cookstyle . foodcritic: <<: *defaults steps: - restore_cache: key: repo-{{ .Environment.CIRCLE_SHA1 }} - - run: chef --version - - run: chef exec foodcritic . + - run: + name: "Chef and Ruby version" + command: | + chef --version + ruby --version + - run: + name: "Check Foodcritic" + command: chef exec foodcritic . chefspec: <<: *defaults steps: - restore_cache: key: repo-{{ .Environment.CIRCLE_SHA1 }} - - run: chef --version - - run: chef exec rspec spec + - run: + name: "Chef and Ruby version" + command: | + chef --version + ruby --version + - run: + name: "Check Serverspec" + command: chef exec rspec spec kitchen: - machine: - services: - - docker - working_directory: ~/cookbook/cookbook_cloudstack + machine: true + services: + - docker steps: - checkout - - run: | - if ! chef -v; then - wget https://packages.chef.io/files/stable/chefdk/3.5.13/ubuntu/18.04/chefdk_3.5.13-1_amd64.deb - sudo dpkg -i chefdk_3.5.13-1_amd64.deb - fi - - run: chef --version - - run: bundle install - - run: berks install - - run: berks update - - run: KITCHEN_LOCAL_YAML=.kitchen.docker.yml kitchen test --destroy always + - run: + name: "Install Chef" + command: | + wget https://packages.chef.io/files/stable/chefdk/3.6.57/ubuntu/18.04/chefdk_3.6.57-1_amd64.deb + sudo dpkg -i chefdk_3.6.57-1_amd64.deb + - run: + name: "Chef and Ruby version" + command: | + chef --version + ruby --version + - run: + name: "Install Gem, Cookbooks dependencies" + command: | + chef exec bundle install + berks install + berks update + - run: + name: "Run Kitchen test" + command: KITCHEN_LOCAL_YAML=.kitchen.docker.yml kitchen test --destroy always workflows: version: 2 diff --git a/.foodcritic b/.foodcritic new file mode 100644 index 0000000..c6a3266 --- /dev/null +++ b/.foodcritic @@ -0,0 +1 @@ +~FC075 \ No newline at end of file diff --git a/.kitchen.docker.yml b/.kitchen.docker.yml new file mode 100644 index 0000000..8f22ca4 --- /dev/null +++ b/.kitchen.docker.yml @@ -0,0 +1,20 @@ +--- +driver: + name: docker + +provisioner: + name: chef_solo + +platforms: + - name: centos-7 + driver_config: + run_command: /usr/sbin/init + privileged: true + provision_command: + - systemctl enable sshd.service + +suites: + - name: default + run_list: + - recipe[test::all-in-one] + attributes: diff --git a/.kitchen.yml b/.kitchen.yml index 211d4e5..8f24435 100644 --- a/.kitchen.yml +++ b/.kitchen.yml @@ -6,11 +6,10 @@ provisioner: name: chef_solo platforms: -# - name: ubuntu-16.04 - name: centos-7 suites: - name: default run_list: - - recipe[cloudstack::circle-ci] + - recipe[test::all-in-one] attributes: diff --git a/Berksfile b/Berksfile index 34fea21..5a7274b 100644 --- a/Berksfile +++ b/Berksfile @@ -1,3 +1,7 @@ source 'https://supermarket.chef.io' metadata + +group :integration do + cookbook 'test', path: 'test/cookbooks/test' +end diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a4705d..deeb87f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,134 +1,138 @@ -cloudstack CHANGELOG -==================== +# cloudstack CHANGELOG -This file is used to list changes made in each version of the co-cloudstack cookbook. +This file is used to list changes made in each version of the cloudstack cookbook. + +## 6.1.0 + +- khos2ow - Remove Provider and enhance Custom Resources +- khos2ow - Fix CircleCI configs + +## 6.0.1 -6.0.1 ------ - khos2ow - Add missing SystemVM URLs for CloudStack 4.10 -6.0.0 ------ +## 6.0.0 + - pdion891 - Fix Chef14 issues on CentOS 7 for cloudstack_setup_database ressource. - - Added circle-ci recipe for automated CI. - - Remove management of sudoers file, pkgs are taking care of it. +- pdion891 - Added circle-ci recipe for automated CI. +- pdion891 - Remove management of sudoers file, pkgs are taking care of it. + +## 5.0.0 -5.0.0 ------ - pdion891 - update default version to 4.11. - khos2ow - fixed cookstyle and foodcritic issues. -4.1.2 ------ +## 4.1.2 + - khos2ow - add default metadata expire to cloudstack repo -4.1.1 ------ +## 4.1.1 + - khos2ow - add server-id for mysql -4.1.0 ------ +## 4.1.0 + - khos2ow - add support for enabling/disabling cloudstack repo -4.0.8 ------ -- put mysql password in single-quotes, otherwise some hardened passwords are not interpreted correctly and logins fail. +## 4.0.8 + +- pdion891 - put mysql password in single-quotes, otherwise some hardened passwords are not interpreted correctly and logins fail. + +## 4.0.7 -4.0.7 ------ - pdion891 - add support for CentOS 7 for ACS 4.10 with JDK8 -4.0.5 ------ +## 4.0.5 + - pdion891 - add repo for mysql-connector-python -4.0.4 ------ +## 4.0.4 + - pdion891 - update apt repo issue for missing pkg signature. - - fix warning for chef 13. +- pdion891 - fix warning for chef 13. + +## 4.0.1 -4.0.1 ------ - pdion891 - update release to 4.8 by default -4.0.0 ------ +## 4.0.0 + - pdion891 - integration to berkshelf and vagrant. - - update for new acs 4.6 release. +- pdion891 - update for new acs 4.6 release. + +## 3.1.1 -3.1.1 ------ - pdion891 - POST for login authentication use to generate admin api_keys - - update systemvm url for acs 4.5 +- pdion891 - update systemvm url for acs 4.5 + +## 3.1.0 -3.1.0 ------ - pdion891 - support cookbook mysql6 -3.0.10 ------- +## 3.0.10 + - pdion891 - fix port_open: localhost-> 127.0.0.1 -3.0.9 ------ +## 3.0.9 + - pdion891 - fix sudoers. -3.0.8 ------ +## 3.0.8 + - pdion891 - update date in header. - - readme typos +- pdion891 - readme typos + +## 3.0.7 -3.0.7 ------ - pdion891 - rename ``hypervisor_tpl`` by ``systemvm`` -3.0.6 ------ +## 3.0.6 + - pdion891 - update 4.4.1 systemplate urls -3.0.5 ------ +## 3.0.5 + - pdion891 - fix systemvmtemplate url selection -3.0.4 ------ +## 3.0.4 + - pdion891 - api_key: major rewrite part1. -3.0.3 ------ +## 3.0.3 + - pdion891 - api_key: add existing keys validation to fix admin password change. -3.0.2 ------ +## 3.0.2 + - pdion891 - Add eventlog to rabbitmq config template -3.0.0 ------ +## 3.0.0 + - pdion891 - Complete rewrite of co-cloudstack as cloudstack using Chef LWRP - pdion891 - Rename cookbook from co-cloudstack to cloudstack. -2.0.3 ------ +## 2.0.3 + - pdion891 - Add mysql-conf to configure mysql-server tunings required by CloudStack. -2.0.2 ------ +## 2.0.2 + - pdion891 - change way of generating APIkeys by querying CloudStack API instead of enabling integration api port. -2.0.0 ------ +## 2.0.0 + - pdion891 - add support for CS 4.3 -1.0.0 ------ +## 1.0.0 + - pdion891 - add vhd-util recipe -- Update license headers -- Update dependencies for opscode cookbooks +- pdion891 - Update license headers +- pdion891 - Update dependencies for opscode cookbooks + +## 0.1.2 -0.1.2 ------ - pdion891 - remove use of databag and use attributes instead. -0.1.0 ------ +## 0.1.0 + - pdion891 - Initial release of co-cloudstack diff --git a/Gemfile b/Gemfile index 6eaae69..6e74a9c 100644 --- a/Gemfile +++ b/Gemfile @@ -16,3 +16,4 @@ gem 'berkshelf' gem 'test-kitchen' gem 'kitchen-vagrant' +gem 'kitchen-docker' diff --git a/README.md b/README.md index 209b8c6..87b6d1e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![Build Status](https://circleci.com/gh/cloudops/cookbook_cloudstack.svg?style=svg)](https://circleci.com/gh/cloudops/cookbook_cloudstack) [![Cookbook Version](https://img.shields.io/cookbook/v/cloudstack.svg)](https://supermarket.chef.io/cookbooks/cloudstack) +[![license](https://img.shields.io/github/license/cloudops/cookbook_cloudstack.svg)](https://github.com/cloudops/cookbook_cloudstack/blob/master/LICENSE) Install and configure [Apache Cloudstack](http://cloudstack.apache.org) using [Chef](http://www.chef.io/). A wrapper cookbook is prefered in order to Install Apache CloudStack properly, refer to [cloudstack_wrapper cookbook](https://github.com/cloudops/cookbook_cloudstack_wrapper) for example. diff --git a/libraries/api_keys.rb b/libraries/api_keys.rb deleted file mode 100644 index 40d6575..0000000 --- a/libraries/api_keys.rb +++ /dev/null @@ -1,180 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Library:: keys -# Author:: Pierre-Luc Dion -# Copyright 2018, CloudOps, Inc. -# -# 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. -# -################################################################################ -# -# methods related to CloudStack API keys. -# -# -module Cloudstack - module ApiKeys - def create_admin_apikeys - # 1. make sure cloudstack is running - # 2. get admin apikeys - # 2.1 does admin keys defines in attributes? - # 2.2 does admin keys found in Chef environment? - # 2.3 does admin keys are already generated in cloudstack? - # 3. if none of the above generate new admin api keys - ################################################################## - if cloudstack_api_is_running? - # bypass the section if CloudStack is not running. - if @current_resource.admin_apikey || @current_resource.admin_secretkey - # if keys attributes are empty search in Chef environment for other node having API-KEYS. - if Chef::Config[:solo] - Chef::Log.warn('This recipe uses search. Chef Solo does not support search.') - other_nodes = [] - else - other_nodes = search(:node, "chef_environment:#{node.chef_environment} AND cloudstack_admin_api_key:* NOT name:#{node.name}") - end - if other_nodes.empty? - admin_apikeys_from_cloudstack - else - @current_resource.admin_apikey(other_nodes.first['cloudstack']['admin']['api_key']) - @current_resource.admin_secretkey(other_nodes.first['cloudstack']['admin']['secret_key']) - if keys_valid? - # API-KEYS from other nodes are valids, so updating current node attributes. - # @current_resource.exists = true - Chef::Log.info "api keys: found valid keys from #{other_nodes.first.name} in the Chef environment: #{node.chef_environment}." - Chef::Log.info 'api keys: updating node attributes' - end - end - elsif keys_valid? - # test API-KEYS on cloudstack, if they work, skip the section. - @current_resource.exists = true - Chef::Log.info 'api keys: are valid, nothing to do.' - else - admin_apikeys_from_cloudstack - end - else - Chef::Log.error 'CloudStack not running, cannot generate API keys.' - end - end - - def admin_apikeys_from_cloudstack - # look if apikeys already exist - # otherwise generate them - if @current_resource.username == 'admin' - admin_keys = retrieve_admin_keys(@current_resource.url, @current_resource.password) - if admin_keys[:api_key].nil? - converge_by('Creating api keys for admin') do - admin_keys = generate_admin_keys(@current_resource.url, @current_resource.password) - Chef::Log.info 'admin api keys: Generate new' - end - else - Chef::Log.info 'admin api keys: use existing in CloudStack' - end - # puts admin_keys - node.normal['cloudstack']['admin']['api_key'] = admin_keys[:api_key] - node.normal['cloudstack']['admin']['secret_key'] = admin_keys[:secret_key] - node.save unless Chef::Config[:solo] - $admin_apikey = admin_keys[:api_key] - $admin_secretkey = admin_keys[:secret_key] - Chef::Log.info "$admin_apikey = #{$admin_apikey}" - else - Chef::Log.error 'Account not admin' - end - end - - def generate_admin_keys(url = 'http://localhost:8080/client/api', password = 'password') - login_params = { command: 'login', username: 'admin', password: password, response: 'json' } - # create sessionkey and cookie of the api session initiated with username and password - uri = URI(url) - uri.query = URI.encode_www_form(login_params) - http = Net::HTTP.new(uri.hostname, uri.port) - res = http.post(uri.request_uri, '') # POST enforced since ACS 4.6 - get_keys_params = { - sessionkey: JSON.parse(res.body)['loginresponse']['sessionkey'], - command: 'registerUserKeys', - response: 'json', - id: '2', - } - - # use sessionkey + cookie to generate admin API and SECRET keys. - uri2 = URI(url) - uri2.query = URI.encode_www_form(get_keys_params) - sleep(2) # add some delay to have the session working - http_cookie = res.response['set-cookie'].split('; ')[0] - http_headers = { 'Cookie': http_cookie } - query_for_keys = http.get(uri2.request_uri, http_headers) - - if query_for_keys.code == '200' - keys = { - api_key: JSON.parse(query_for_keys.body)['registeruserkeysresponse']['userkeys']['apikey'], - secret_key: JSON.parse(query_for_keys.body)['registeruserkeysresponse']['userkeys']['secretkey'], - } - else - Chef::Log.info "Error creating keys errorcode: #{query_for_keys.code}" - end - keys - end - - def keys_valid? - # Test if current defined keys from Chef are valid - # - if @current_resource.admin_apikey || @current_resource.admin_secretkey - # return false if one key is empty - require 'cloudstack_ruby_client' - begin - client = CloudstackRubyClient::Client.new(@current_resource.url, @current_resource.admin_apikey, @current_resource.admin_secretkey, @current_resource.ssl) - list_apis = client.list_apis - rescue - false - end - if list_apis.nil? - false - else - true - end - else - false - end - end - - def retrieve_admin_keys(url = 'http://localhost:8080/client/api', password = 'password') - login_params = { command: 'login', username: 'admin', password: password, response: 'json' } - # create sessionkey and cookie of the api session initiated with username and password - uri = URI(url) - uri.query = URI.encode_www_form(login_params) - http = Net::HTTP.new(uri.hostname, uri.port) - res = http.post(uri.request_uri, '') # POST enforced since ACS 4.6 - get_keys_params = { - sessionkey: JSON.parse(res.body)['loginresponse']['sessionkey'], - command: 'listUsers', - response: 'json', - id: '2', - } - # use sessionkey + cookie to generate admin API and SECRET keys. - uri2 = URI(url) - uri2.query = URI.encode_www_form(get_keys_params) - sleep(2) # add some delay to have the session working - http_cookie = res.response['set-cookie'].split('; ')[0] - http_headers = { 'Cookie': http_cookie } - users = http.get(uri2.request_uri, http_headers) - if users.code == '200' - keys = { - api_key: JSON.parse(users.body)['listusersresponse']['user'].first['apikey'], - secret_key: JSON.parse(users.body)['listusersresponse']['user'].first['secretkey'], - } - else - Chef::Log.info "Error creating keys errorcode: #{users.code}" - end - keys - end - end -end diff --git a/libraries/global_setting.rb b/libraries/global_setting.rb deleted file mode 100644 index 596f0aa..0000000 --- a/libraries/global_setting.rb +++ /dev/null @@ -1,44 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Library:: global_setting -# Author:: Pierre-Luc Dion -# Copyright 2018, CloudOps, Inc. -# -# 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. -# - -module Cloudstack - module GlobalSetting - # Support whyrun - def whyrun_supported? - false - end - - def load_current_value(name) - require 'cloudstack_ruby_client' - # get CloudStack current value of the Global Setting - client = CloudstackRubyClient::Client.new('http://localhost:8080/client/api/', @current_resource.admin_apikey, @current_resource.admin_secretkey, false) - client.list_configurations(name: name)['configuration'].first['value'] - end - - def update_setting(name, value) - require 'cloudstack_ruby_client' - client = CloudstackRubyClient::Client.new('http://localhost:8080/client/api/', @current_resource.admin_apikey, @current_resource.admin_secretkey, false) - client.update_configuration( - name: name, - value: value - ) - end - end -end diff --git a/libraries/system_template.rb b/libraries/system_template.rb deleted file mode 100644 index 7005e24..0000000 --- a/libraries/system_template.rb +++ /dev/null @@ -1,58 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Library:: system_template -# Author:: Pierre-Luc Dion -# Copyright 2018, CloudOps, Inc. -# -# 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. -# - -module Cloudstack - module SystemTemplate - # retrieve template ID from database - def db_template_id - # get template ID from database to check path - Chef::Log.debug 'Retrieve template ID from database' - template_cmd = "mysql -h #{@current_resource.db_host} --user=#{@current_resource.db_user} --password=#{@current_resource.db_password} --skip-column-names -U cloud -e 'select max(id) from cloud.vm_template where type = \"SYSTEM\" and hypervisor_type = \"#{@current_resource.hypervisor}\" and removed is null'" - template_id = Mixlib::ShellOut.new(template_cmd) - template_id.run_command - Chef::Log.debug "template id = #{template_id.stdout.chomp}" - template_id.stdout.chomp - end - - # Create or mount secondary storage path - def secondary_storage - unless ::File.exist?(@current_resource.nfs_path) - # Use condition enclosing instead of 'not_if' guard because resource nfs_path is not defined in case of resource merging - directory @current_resource.nfs_path do - owner 'root' - group 'root' - action :create - recursive true - end - end - end - - def download_systemvm_template - # Create database configuration for cloudstack management server that will use and existing database. - # puts "Downloading system template from: #{@current_resource.url}" - Chef::Log.info "Downloading system template for #{@current_resource.hypervisor}, this will take some time..." - download_cmd = "/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt -m #{@current_resource.nfs_path} -u #{@current_resource.url} -h #{@current_resource.hypervisor} -F" - download_template = Mixlib::ShellOut.new(download_cmd) - download_template.run_command - if download_template.exitstatus == 0 - end - end - end -end diff --git a/metadata.rb b/metadata.rb index 0623cfc..eab985a 100644 --- a/metadata.rb +++ b/metadata.rb @@ -4,15 +4,15 @@ license 'Apache-2.0' description 'Installs/Configures cloudstack' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '6.0.1' +version '6.1.0' source_url 'https://github.com/cloudops/cookbook_cloudstack' issues_url 'https://github.com/cloudops/cookbook_cloudstack/issues' -depends 'yum' , '> 3.0' -depends 'apt' , '> 2.0' +depends 'yum', '> 3.0' +depends 'apt', '> 2.0' depends 'mysql', '~> 8.0' -depends 'sudo' , '>= 2.6.0' +depends 'sudo', '>= 2.6.0' supports 'centos' supports 'redhat' diff --git a/providers/api_keys.rb b/providers/api_keys.rb deleted file mode 100644 index 5ff8e80..0000000 --- a/providers/api_keys.rb +++ /dev/null @@ -1,81 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Provider:: api_keys -# Author:: Pierre-Luc Dion () -# Copyright 2018, CloudOps, Inc. -# -# 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. -# -################################################################################ -# Generate api keys for specified CloudStack user. -# currently work for admin account. - -require 'uri' -require 'net/http' -require 'json' - -include Cloudstack::Helper -include Cloudstack::ApiKeys - -# Support whyrun -def whyrun_supported? - true -end - -use_inline_resources if defined?(:use_inline_resources) # ~FC113 - -######### -# ACTIONS -######### - -action :create do - wait_count = 0 - until cloudstack_api_is_running? || wait_count == 5 - cloudstack_api_is_running? - sleep(5) - wait_count += 1 - Chef::Log.info 'Waiting CloudStack to start' if wait_count == 1 - end - - create_admin_apikeys -end - -action :reset do - # force generate new API keys - # load_current_resource - if cloudstack_is_running? - if @current_resource.username == 'admin' - converge_by('Reseting admin api keys') do - admin_keys = generate_admin_keys(@current_resource.url, @current_resource.password) - Chef::Log.info 'admin api keys: Generate new' - node.normal['cloudstack']['admin']['api_key'] = admin_keys[:api_key] - node.normal['cloudstack']['admin']['secret_key'] = admin_keys[:secret_key] - node.save unless Chef::Config[:solo] - $admin_apikey = admin_keys[:api_key] - $admin_secretkey = admin_keys[:secret_key] - end - end - else - Chef::Log.error 'CloudStack not running, cannot generate API keys.' - end -end - -def load_current_resource - @current_resource = Chef::Resource::CloudstackApiKeys.new(@new_resource.name) - @current_resource.username(@new_resource.name) - @current_resource.password(@new_resource.password) - @current_resource.url(@new_resource.url) - @current_resource.admin_apikey(@new_resource.admin_apikey) - @current_resource.admin_secretkey(@new_resource.admin_secretkey) - @current_resource.ssl(@new_resource.ssl) -end diff --git a/providers/global_setting.rb b/providers/global_setting.rb deleted file mode 100644 index 78f1e64..0000000 --- a/providers/global_setting.rb +++ /dev/null @@ -1,72 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Provider:: global_setting -# Author:: Pierre-Luc Dion () -# Copyright 2018, CloudOps, Inc. -# -# 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. -# -# Configure Global Settings -############################################################################### - -# require 'cloudstack_ruby_client' -require 'json' -include Cloudstack::Helper -include Cloudstack::GlobalSetting - -use_inline_resources if defined?(:use_inline_resources) # ~FC113 - -######### -# ACTIONS -######### -action :update do - unless @current_resource.admin_apikey.nil? - unless @current_resource.exists - converge_by("Update Global Setting: #{@current_resource.name} to #{@current_resource.value}") do - # test_connection?(@current_resource.admin_apikey, @current_resource.admin_secretkey) - update_setting(@current_resource.name, @current_resource.value) - end - end - end -end - -def load_current_resource - require 'cloudstack_ruby_client' - @current_resource = Chef::Resource::CloudstackGlobalSetting.new(@new_resource.name) - @current_resource.name(@new_resource.name) - if $admin_apikey.nil? - @current_resource.admin_apikey(@new_resource.admin_apikey) - @current_resource.admin_secretkey(@new_resource.admin_secretkey) - else # if it's the first run on the server $admin_apikey will not be empty - @current_resource.admin_apikey($admin_apikey) - @current_resource.admin_secretkey($admin_secretkey) - end - @current_resource.value(@new_resource.value) - - if cloudstack_is_running? - if @current_resource.admin_apikey.nil? - Chef::Log.error 'admin_apikey empty, cannot update Global Settings' - else - current_value = load_current_value(@current_resource.name) - if current_value.nil? - Chef::Log.error "Global Setting: #{@current_resource.name} not found" - elsif @current_resource.value == current_value - @current_resource.exists = true - else - @current_resource.exists = false - end - end - else - Chef::Log.error 'CloudStack not running, cannot update Global Settings.' - end -end diff --git a/providers/system_template.rb b/providers/system_template.rb deleted file mode 100644 index 153dec8..0000000 --- a/providers/system_template.rb +++ /dev/null @@ -1,72 +0,0 @@ -# -# Cookbook Name:: cloudstack -# Provider:: system_template -# Author:: Pierre-Luc Dion () -# Copyright 2018, CloudOps, Inc. -# -# 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. -# -# Download system template for initial deployment of CloudStack - -include Chef::Mixin::ShellOut -include Cloudstack::Helper -include Cloudstack::SystemTemplate - -use_inline_resources if defined?(:use_inline_resources) # ~FC113 - -action :create do - load_current_resource - - # Chef::Log.info 'creating cloudstack database' - unless @current_resource.exists - converge_by("Downloading system template from: #{@current_resource.url}") do - # test_connection?(@current_resource.admin_apikey, @current_resource.admin_secretkey) - secondary_storage - download_systemvm_template - end - end -end - -def load_current_resource - @current_resource = Chef::Resource::CloudstackSystemTemplate.new(@new_resource.name) - @current_resource.name(@new_resource.name) - @current_resource.url(@new_resource.url) - @current_resource.hypervisor(@new_resource.hypervisor) - @current_resource.nfs_path(@new_resource.nfs_path) - @current_resource.nfs_server(@new_resource.nfs_server) - @current_resource.db_user(@new_resource.db_user) - @current_resource.db_password(@new_resource.db_password) - @current_resource.db_host(@new_resource.db_host) - - # if CloudStack management-server is running, it mean a systemvm template as been downloaded. - if cloudstack_is_running? - @current_resource.exists = true - elsif db_exist?(@current_resource.db_host, @current_resource.db_user, @current_resource.db_password) - if @current_resource.url.nil? - cmd = Mixlib::ShellOut.new("mysql -h #{@current_resource.db_host} --user=#{@current_resource.db_user} --password='#{@current_resource.db_password}' --skip-column-names -U cloud -e 'select max(url) from cloud.vm_template where type = \"SYSTEM\" and hypervisor_type = \"#{@current_resource.hypervisor}\" and removed is null'") - cmd.run_command - cmd.error! - @current_resource.url(cmd.stdout.chomp) - end - template_id = db_template_id - Chef::Log.debug "looking for template in #{@current_resource.nfs_path}/template/tmpl/1/#{template_id}" - if ::File.exist?("#{@current_resource.nfs_path}/template/tmpl/1/#{template_id}/template.properties") - Chef::Log.debug "template exists in #{@current_resource.nfs_path}/template/tmpl/1/#{template_id}" - @current_resource.exists = true - else - @current_resource.exists = false - end - else - Chef::Log.error 'Database not configured. Cannot retrieve Template URL' - end -end diff --git a/recipes/management_server.rb b/recipes/management_server.rb index 5f1dfef..2a23875 100644 --- a/recipes/management_server.rb +++ b/recipes/management_server.rb @@ -36,4 +36,3 @@ end include_recipe 'cloudstack::vhd-util' - diff --git a/recipes/repo_rhel.rb b/recipes/repo_rhel.rb index a7308af..c3fc265 100644 --- a/recipes/repo_rhel.rb +++ b/recipes/repo_rhel.rb @@ -20,19 +20,19 @@ include_recipe 'yum' yum_repository 'cloudstack' do - description 'Apache Cloudstack' - baseurl node['cloudstack']['repo_url'] - enabled node['cloudstack']['repo_enabled'] + description 'Apache Cloudstack' + baseurl node['cloudstack']['repo_url'] + enabled node['cloudstack']['repo_enabled'] metadata_expire node['cloudstack']['repo_metadata_expire'] - gpgkey node['cloudstack']['repo_sign'] - gpgcheck node['cloudstack']['repo_sign'].empty? ? false : true - action :create + gpgkey node['cloudstack']['repo_sign'] + gpgcheck node['cloudstack']['repo_sign'].empty? ? false : true + action :create end yum_repository 'mysql-connector' do description 'MySQL Community connectors' - baseurl 'http://repo.mysql.com/yum/mysql-connectors-community/el/$releasever/$basearch/' - gpgkey 'http://repo.mysql.com/RPM-GPG-KEY-mysql' - gpgcheck true - action :create + baseurl 'http://repo.mysql.com/yum/mysql-connectors-community/el/$releasever/$basearch/' + gpgkey 'http://repo.mysql.com/RPM-GPG-KEY-mysql' + gpgcheck true + action :create end diff --git a/recipes/repo_ubuntu.rb b/recipes/repo_ubuntu.rb index fbb01a2..df906a2 100644 --- a/recipes/repo_ubuntu.rb +++ b/recipes/repo_ubuntu.rb @@ -21,10 +21,10 @@ # add Apache CloudStack .deb repo apt_repository 'cloudstack' do - uri node['cloudstack']['repo_url'] - components [ node['cloudstack']['release_major'] ] + uri node['cloudstack']['repo_url'] + components [ node['cloudstack']['release_major'] ] distribution 'trusty' - trusted node['cloudstack']['repo_trust'] + trusted node['cloudstack']['repo_trust'] unless node['cloudstack']['repo_sign'].empty? key node['cloudstack']['repo_sign'] end diff --git a/recipes/usage.rb b/recipes/usage.rb index fc9139e..fca4809 100644 --- a/recipes/usage.rb +++ b/recipes/usage.rb @@ -22,7 +22,9 @@ package 'cloudstack-usage' do action :install - # only_if { node.recipes.include?('cloudstack::management_server') } + unless node['cloudstack']['version'].empty? + version node['cloudstack']['version'] + end end service 'cloudstack-usage' do diff --git a/recipes/vhd-util.rb b/recipes/vhd-util.rb index 89f31c5..0170ac3 100644 --- a/recipes/vhd-util.rb +++ b/recipes/vhd-util.rb @@ -21,7 +21,7 @@ remote_file "#{node['cloudstack']['vhd-util_path']}/vhd-util" do source node['cloudstack']['vhd-util_url'] - mode 0755 + mode 0755 action :create_if_missing only_if { node['recipes'].include?('cloudstack::management_server') } end diff --git a/resources/api_keys.rb b/resources/api_keys.rb index 188d00e..c68dc08 100644 --- a/resources/api_keys.rb +++ b/resources/api_keys.rb @@ -17,15 +17,204 @@ # limitations under the License. # -actions :create, :reset - default_action :create -attribute :username, name_attribute: true, kind_of: String -attribute :url, kind_of: String, default: 'http://localhost:8080/client/api' -attribute :password, kind_of: String, default: 'password' -attribute :admin_apikey, kind_of: String -attribute :admin_secretkey, kind_of: String -attribute :ssl, equal_to: [true, false, 'true', 'false'], default: false +property :username, String, name_property: true +property :url, String, default: 'http://localhost:8080/client/api' +property :password, String, default: 'password' +property :admin_apikey, String +property :admin_secretkey, String +property :ssl, equal_to: [true, false, 'true', 'false'], default: false + +actions :create do + wait_count = 0 + until cloudstack_api_is_running? || wait_count == 5 + cloudstack_api_is_running? + sleep(5) + wait_count += 1 + Chef::Log.info 'Waiting CloudStack to start' if wait_count == 1 + end + + if cloudstack_api_is_running? + create_admin_apikeys + else + Chef::Log.error 'CloudStack not running, cannot generate API keys.' + end +end + +actions :reset do + # force generate new API keys + # load_current_resource + if cloudstack_is_running? + if new_resource.username == 'admin' + converge_by('Reseting admin api keys') do + admin_keys = generate_admin_keys(new_resource.url, new_resource.password) + Chef::Log.info 'admin api keys: Generate new' + node.normal['cloudstack']['admin']['api_key'] = admin_keys[:api_key] + node.normal['cloudstack']['admin']['secret_key'] = admin_keys[:secret_key] + node.save unless Chef::Config[:solo] + new_resource.admin_apikey = admin_keys[:api_key] + new_resource.admin_secretkey = admin_keys[:secret_key] + end + end + else + Chef::Log.error 'CloudStack not running, cannot generate API keys.' + end +end + +action_class do + require 'uri' + require 'net/http' + require 'json' + + include Cloudstack::Helper + + def create_admin_apikeys + # 1. make sure cloudstack is running + # 2. get admin apikeys + # 2.1 does admin keys defines in attributes? + # 2.2 does admin keys found in Chef environment? + # 2.3 does admin keys are already generated in cloudstack? + # 3. if none of the above generate new admin api keys + ################################################################## + # bypass the section if CloudStack is not running. + if new_resource.admin_apikey || new_resource.admin_secretkey + # if keys attributes are empty search in Chef environment for other node having API-KEYS. + if Chef::Config[:solo] + Chef::Log.warn('This recipe uses search. Chef Solo does not support search.') + other_nodes = [] + else + other_nodes = search(:node, "chef_environment:#{node.chef_environment} AND cloudstack_admin_api_key:* NOT name:#{node.name}") + end + if other_nodes.empty? + admin_apikeys_from_cloudstack + else + new_resource.admin_apikey(other_nodes.first['cloudstack']['admin']['api_key']) + new_resource.admin_secretkey(other_nodes.first['cloudstack']['admin']['secret_key']) + if keys_valid? + # API-KEYS from other nodes are valids, so updating current node attributes. + # new_resource.exists = true + Chef::Log.info "api keys: found valid keys from #{other_nodes.first.name} in the Chef environment: #{node.chef_environment}." + Chef::Log.info 'api keys: updating node attributes' + end + end + elsif keys_valid? + # test API-KEYS on cloudstack, if they work, skip the section. + new_resource.exists = true + Chef::Log.info 'api keys: are valid, nothing to do.' + else + admin_apikeys_from_cloudstack + end + end + + def admin_apikeys_from_cloudstack + # look if apikeys already exist + # otherwise generate them + if new_resource.username == 'admin' + admin_keys = retrieve_admin_keys(new_resource.url, new_resource.password) + if admin_keys[:api_key].nil? + converge_by('Creating api keys for admin') do + admin_keys = generate_admin_keys(new_resource.url, new_resource.password) + Chef::Log.info 'admin api keys: Generate new' + end + else + Chef::Log.info 'admin api keys: use existing in CloudStack' + end + # puts admin_keys + node.normal['cloudstack']['admin']['api_key'] = admin_keys[:api_key] + node.normal['cloudstack']['admin']['secret_key'] = admin_keys[:secret_key] + node.save unless Chef::Config[:solo] + new_resource.admin_apikey = admin_keys[:api_key] + new_resource.admin_secretkey = admin_keys[:secret_key] + Chef::Log.info "$admin_apikey = #{new_resource.admin_apikey}" + else + Chef::Log.error 'Account not admin' + end + end + + def generate_admin_keys(url = 'http://localhost:8080/client/api', password = 'password') + login_params = { command: 'login', username: 'admin', password: password, response: 'json' } + # create sessionkey and cookie of the api session initiated with username and password + uri = URI(url) + uri.query = URI.encode_www_form(login_params) + http = Net::HTTP.new(uri.hostname, uri.port) + res = http.post(uri.request_uri, '') # POST enforced since ACS 4.6 + get_keys_params = { + sessionkey: JSON.parse(res.body)['loginresponse']['sessionkey'], + command: 'registerUserKeys', + response: 'json', + id: '2', + } + + # use sessionkey + cookie to generate admin API and SECRET keys. + uri2 = URI(url) + uri2.query = URI.encode_www_form(get_keys_params) + sleep(2) # add some delay to have the session working + http_cookie = res.response['set-cookie'].split('; ')[0] + http_headers = { 'Cookie': http_cookie } + query_for_keys = http.get(uri2.request_uri, http_headers) + + if query_for_keys.code == '200' + keys = { + api_key: JSON.parse(query_for_keys.body)['registeruserkeysresponse']['userkeys']['apikey'], + secret_key: JSON.parse(query_for_keys.body)['registeruserkeysresponse']['userkeys']['secretkey'], + } + else + Chef::Log.info "Error creating keys errorcode: #{query_for_keys.code}" + end + keys + end + + def keys_valid? + # Test if current defined keys from Chef are valid + # + if new_resource.admin_apikey || new_resource.admin_secretkey + # return false if one key is empty + require 'cloudstack_ruby_client' + begin + client = CloudstackRubyClient::Client.new(new_resource.url, new_resource.admin_apikey, new_resource.admin_secretkey, new_resource.ssl) + list_apis = client.list_apis + rescue + false + end + if list_apis.nil? + false + else + true + end + else + false + end + end -attr_accessor :exists + def retrieve_admin_keys(url = 'http://localhost:8080/client/api', password = 'password') + login_params = { command: 'login', username: 'admin', password: password, response: 'json' } + # create sessionkey and cookie of the api session initiated with username and password + uri = URI(url) + uri.query = URI.encode_www_form(login_params) + http = Net::HTTP.new(uri.hostname, uri.port) + res = http.post(uri.request_uri, '') # POST enforced since ACS 4.6 + get_keys_params = { + sessionkey: JSON.parse(res.body)['loginresponse']['sessionkey'], + command: 'listUsers', + response: 'json', + id: '2', + } + # use sessionkey + cookie to generate admin API and SECRET keys. + uri2 = URI(url) + uri2.query = URI.encode_www_form(get_keys_params) + sleep(2) # add some delay to have the session working + http_cookie = res.response['set-cookie'].split('; ')[0] + http_headers = { 'Cookie': http_cookie } + users = http.get(uri2.request_uri, http_headers) + if users.code == '200' + keys = { + api_key: JSON.parse(users.body)['listusersresponse']['user'].first['apikey'], + secret_key: JSON.parse(users.body)['listusersresponse']['user'].first['secretkey'], + } + else + Chef::Log.info "Error creating keys errorcode: #{users.code}" + end + keys + end +end diff --git a/resources/global_setting.rb b/resources/global_setting.rb index 9d3780c..ddd6c60 100644 --- a/resources/global_setting.rb +++ b/resources/global_setting.rb @@ -19,13 +19,56 @@ # Configure Global Settings ############################################################################### -actions :update - default_action :update -attribute :name, name_attribute: true, kind_of: String -attribute :value, kind_of: String -attribute :admin_apikey, kind_of: String -attribute :admin_secretkey, kind_of: String +property :name, String, name_property: true +property :value, String +property :admin_apikey, String +property :admin_secretkey, String + +actions :update do + unless new_resource.admin_apikey.nil? + unless current_value_exists? + converge_by("Update Global Setting: #{new_resource.name} to #{new_resource.value}") do + # test_connection?(new_resource.admin_apikey, new_resource.admin_secretkey) + update_setting(new_resource.name, new_resource.value) + end + end + end +end + +action_class do + require 'json' + include Cloudstack::Helper + + def load_current_value(name) + # get CloudStack current value of the Global Setting + client = CloudstackRubyClient::Client.new('http://localhost:8080/client/api/', new_resource.admin_apikey, new_resource.admin_secretkey, false) + client.list_configurations(name: name)['configuration'].first['value'] + end + + def update_setting(name, value) + client = CloudstackRubyClient::Client.new('http://localhost:8080/client/api/', new_resource.admin_apikey, new_resource.admin_secretkey, false) + client.update_configuration( + name: name, + value: value + ) + end -attr_accessor :exists + def current_value_exists? + if cloudstack_is_running? + if new_resource.admin_apikey.nil? || new_resource.admin_secretkey.nil? + Chef::Log.error 'admin_apikey empty, cannot update Global Settings' + else + current_value = load_current_value(new_resource.name) + if current_value.nil? + Chef::Log.error "Global Setting: #{new_resource.name} not found" + else + new_resource.value == current_value + end + end + else + Chef::Log.error 'CloudStack not running, cannot update Global Settings.' + end + end +end diff --git a/resources/system_template.rb b/resources/system_template.rb index b49998c..d95ce13 100644 --- a/resources/system_template.rb +++ b/resources/system_template.rb @@ -16,18 +16,80 @@ # See the License for the specific language governing permissions and # limitations under the License. -actions :create - default_action :create -attribute :hypervisor, name_attribute: true, kind_of: String -attribute :nfs_path, kind_of: String, default: '/mnt/secondary' -attribute :nfs_server, kind_of: String, default: 'localhost' +property :hypervisor, String, name_property: true +property :nfs_path, String, default: '/mnt/secondary' +property :nfs_server, String, default: 'localhost' # If URL net specify, can retreive template URL from database. -attribute :url, kind_of: String -attribute :db_user, kind_of: String, default: 'cloud' # node['cloudstack']['db']['user'] -attribute :db_password, kind_of: String, default: 'password' # node['cloudstack']['db']['password'] -attribute :db_host, kind_of: String, default: 'localhost' # node['cloudstack']['db']['host'] +property :url, String +property :db_user, String, default: 'cloud' # node['cloudstack']['db']['user'] +property :db_password, String, default: 'cloud' # node['cloudstack']['db']['password'] +property :db_host, String, default: 'localhost' # node['cloudstack']['db']['host'] + +action :create do + unless current_value_exists? + converge_by("Creating secondary storage at: #{new_resource.nfs_path}") do + secondary_storage + end + converge_by("Downloading system template from: #{new_resource.url}") do + download_systemvm_template + end + end +end + +action_class do + include Cloudstack::Helper + + def current_value_exists? + # if CloudStack management-server is running, it mean a systemvm template has been downloaded. + if cloudstack_is_running? + true + elsif db_exist?(new_resource.db_host, new_resource.db_user, new_resource.db_password) + if new_resource.url.nil? + cmd = shell_out!("mysql -h #{new_resource.db_host} --user=#{new_resource.db_user} --password='#{new_resource.db_password}' --skip-column-names -U cloud -e 'select max(url) from cloud.vm_template where type = \"SYSTEM\" and hypervisor_type = \"#{new_resource.hypervisor}\" and removed is null'") + cmd.error! + new_resource.url(cmd.stdout.chomp) + end + template_id = db_template_id + Chef::Log.debug "looking for template in #{new_resource.nfs_path}/template/tmpl/1/#{template_id}" + if ::File.exist?("#{new_resource.nfs_path}/template/tmpl/1/#{template_id}/template.properties") + Chef::Log.debug "template exists in #{new_resource.nfs_path}/template/tmpl/1/#{template_id}" + true + else + false + end + else + Chef::Log.error 'Database not configured. Cannot retrieve Template URL' + false + end + end + + # retrieve template ID from database + def db_template_id + # get template ID from database to check path + Chef::Log.debug 'Retrieve template ID from database' + template_id = shell_out!("mysql -h #{new_resource.db_host} --user=#{new_resource.db_user} --password=#{new_resource.db_password} --skip-column-names -U cloud -e 'select max(id) from cloud.vm_template where type = \"SYSTEM\" and hypervisor_type = \"#{new_resource.hypervisor}\" and removed is null'") + Chef::Log.debug "template id = #{template_id.stdout.chomp}" + template_id.stdout.chomp + end + + # Create or mount secondary storage path + def secondary_storage + unless ::File.exist?(new_resource.nfs_path) + shell_out!("mkdir -p #{new_resource.nfs_path}") + shell_out!("chown -R root:root #{new_resource.nfs_path}") + end + end -attr_accessor :exists + def download_systemvm_template + # Create database configuration for cloudstack management server that will use and existing database. + # puts "Downloading system template from: #{new_resource.url}" + Chef::Log.info "Downloading system template for #{new_resource.hypervisor}, this will take some time..." + download_cmd = "/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt -m #{new_resource.nfs_path} -u #{new_resource.url} -h #{new_resource.hypervisor} -F" + shell_out!(download_cmd) + # if download_template.exitstatus == 0 + # end + end +end diff --git a/spec/recipes/default_spec.rb b/spec/recipes/default_spec.rb index e2bcf30..332033c 100644 --- a/spec/recipes/default_spec.rb +++ b/spec/recipes/default_spec.rb @@ -1,27 +1,10 @@ -require_relative '../spec_helper' +require 'spec_helper' -describe 'cloudstack::default' do - subject { ChefSpec::Runner.new.converge(described_recipe) } - - # Write quick specs using `it` blocks with implied subjects - it { should do_something('...') } - - # Write full examples using the `expect` syntax - it 'does something' do - expect(subject).to do_something('...') +describe 'cloudstack::management_server' do + let(:chef_run) do + ChefSpec::ServerRunner.new(platform: 'centos', version: '7.4.1708').converge(described_recipe) end - # Use an explicit subject - let(:chef_run) { ChefSpec::Runner.new.converge(described_recipe) } - - it 'does something' do - expect(chef_run).to do_something('...') - end -end - -describe 'cloudstack::cloudstack_management' do - let(:chef_run) { ChefSpec::Runner.new.converge(described_recipe) } - it 'installs cloudstack-management' do expect(chef_run).to install_package('cloudstack-management') end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1995e2b..c9a3f38 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,25 +1,3 @@ # Added by ChefSpec require 'chefspec' - -# Uncomment to use ChefSpec's Berkshelf extension -# require 'chefspec/berkshelf' - -RSpec.configure do |config| - # Specify the path for Chef Solo to find cookbooks - # config.cookbook_path = '/var/cookbooks' - - # Specify the path for Chef Solo to find roles - # config.role_path = '/var/roles' - - # Specify the Chef log_level (default: :warn) - # config.log_level = :debug - - # Specify the path to a local JSON file with Ohai data - # config.path = 'ohai.json' - - # Specify the operating platform to mock Ohai data from - # config.platform = 'ubuntu' - - # Specify the operating version to mock Ohai data from - # config.version = '12.04' -end +require 'chefspec/berkshelf' diff --git a/test/cookbooks/test/metadata.rb b/test/cookbooks/test/metadata.rb new file mode 100644 index 0000000..f0926a1 --- /dev/null +++ b/test/cookbooks/test/metadata.rb @@ -0,0 +1,4 @@ +name 'test' +version '0.0.1' + +depends 'cloudstack' diff --git a/recipes/circle-ci.rb b/test/cookbooks/test/recipes/all-in-one.rb similarity index 75% rename from recipes/circle-ci.rb rename to test/cookbooks/test/recipes/all-in-one.rb index f1f9fd7..01720a2 100644 --- a/recipes/circle-ci.rb +++ b/test/cookbooks/test/recipes/all-in-one.rb @@ -1,6 +1,6 @@ # -# Cookbook Name:: cloudstack -# Recipe:: default +# Cookbook Name:: test +# Recipe:: all # Author:: Pierre-Luc Dion () # Copyright 2018, CloudOps, Inc. # @@ -9,14 +9,14 @@ include_recipe 'cloudstack::management_server' include_recipe 'cloudstack::usage' -# install mysql-server -package 'mariadb-server' - execute 'set mysql root password' do command 'mysqladmin -h 127.0.0.1 -u root password password' action :nothing end +# install mysql-server +package 'mariadb-server' + if platform?(%w(redhat centos fedora oracle)) service 'mariadb' do action :start @@ -38,6 +38,16 @@ action :create end +# download initial systemvm template +cloudstack_system_template 'xenserver' do + nfs_path node['cloudstack']['secondary']['path'] + nfs_server node['cloudstack']['secondary']['host'] + url node['cloudstack']['systemvm']['xenserver'] + db_user 'cloud' + db_password 'cloud' + action :create +end + cloudstack_setup_management node.name do tomcat7 true end diff --git a/test/integration/default/serverspec/systemvm_spec.rb b/test/integration/default/serverspec/systemvm_spec.rb new file mode 100644 index 0000000..7ec7ee5 --- /dev/null +++ b/test/integration/default/serverspec/systemvm_spec.rb @@ -0,0 +1,24 @@ +require 'serverspec' +set :backend, :exec + +describe file('/data/secondary') do + it { should be_directory } +end + +describe file('/data/secondary/template/tmpl/1/1') do + it { should be_directory } +end + +describe file('/data/secondary/template/tmpl/1/1/template.properties') do + it { should be_file } + it { should exist } +end + +# describe file('/data/primary') do +# it { should be_directory } +# end + +# describe service('nfs-server') do +# it { should be_enabled } +# it { should be_running } +# end