diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7ee44a --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.bundle/ +berks-cookbooks/ +.kitchen +.vagrant +.coverage/ +*.swp +Berksfile.lock +Vagrantfile +Gemfile.lock diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..a90befc --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,25 @@ +inherit_from: .rubocop_todo.yml + +AllCops: + Include: + - metadata.rb + - Gemfile + - attributes/** + - libraries/** + - providers/** + - recipes/** + - resources/** + - spec/** + Exclude: + - .cookbooks/**/* + - berks-cookbooks/**/* + - .bundle/**/* + +Encoding: + Enabled: false + +NumericLiterals: + Enabled: false + +LineLength: + Max: 200 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..dd8445b --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,38 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2016-09-27 14:38:25 +0200 using RuboCop version 0.39.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 2 +Lint/ParenthesesAsGroupedExpression: + Exclude: + - 'recipes/dashboard.rb' + +# Offense count: 3 +# Configuration parameters: EnforcedStyle, SupportedStyles. +# SupportedStyles: nested, compact +Style/ClassAndModuleChildren: + Exclude: + - 'recipes/dashboard.rb' + - 'recipes/openrc.rb' + - 'recipes/server.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly. +# SupportedStyles: assign_to_condition, assign_inside_condition +Style/ConditionalAssignment: + Exclude: + - 'recipes/server.rb' + +# Offense count: 3 +Style/Documentation: + Exclude: + - 'spec/**/*' + - 'test/**/*' + - 'recipes/dashboard.rb' + - 'recipes/openrc.rb' + - 'recipes/server.rb' diff --git a/Berksfile b/Berksfile new file mode 100644 index 0000000..49609c2 --- /dev/null +++ b/Berksfile @@ -0,0 +1,12 @@ +source 'https://supermarket.chef.io' + +metadata + +cookbook "openstack-identity", + github: "openstack/cookbook-openstack-identity" +cookbook "openstack-common", + github: "openstack/cookbook-openstack-common" +cookbook "openstack-ops-messaging", + github: "openstack/cookbook-openstack-ops-messaging" +cookbook "openstackclient", + github: "cloudbau/cookbook-openstackclient" diff --git a/README.md b/README.md new file mode 100644 index 0000000..84db79c --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +Description +=========== + +This cookbook installs the OpenStack Application Catalog Service +**Murano** as part of the OpenStack reference deployment Chef for OpenStack. The +https://github.com/openstack/openstack-chef-repo contains documentation for +using this cookbook in the context of a full OpenStack deployment. Murano is +installed from packages, creating the default user, tenant, and roles. It also +registers the application-catalog service and application-catalog endpoint. + +http://docs.openstack.org/developer/murano/ + +Requirements +============ + +- Chef 12 or higher +- chefdk 0.9.0 for testing (also includes berkshelf for cookbook dependency + resolution) + +Platform +======== + +- ubuntu + +Cookbooks +========= + +The following cookbooks are dependencies: + +- openstack-common (>=14.0.0) +- openstack-identity (>=14.0.0) +- openstack-ops-messaging (>=14.0.0) +- 'openstackclient', '>= 0.1.0' +- apache2 + +Attributes +========== + +Please see the extensive inline documentation in `attributes/*.rb` for +descriptions of all the settable attributes for this cookbook. + +Note that all attributes are in the `default['openstack']` "namespace" + +The usage of attributes to generate the murano.conf is decribed in the +openstack-common cookbook. + +Providers +========= + +application +----- + +Action: `:create` + +- `:name`: A name of the application. +- `:category`: Application category. +- `:is_public`: True/False - should application be public or user specific. +- `:identity_user`: Username of the Keystone admin user. +- `:identity_pass`: Password for the Keystone admin user. +- `:identity_tenant`: Name of the Keystone admin user's tenant. +- `:identity_uri`: URI of the Identity API endpoint. + +Recipes +======= + +## openstack-application-catalog::server +- Installs the Openstack application-catalog service + +## openstack-application-catalog::dashboard +- Extends Horizon service to include Murano + +## openstack-application-catalog::openrc +- Creates muranorc file + + +License and Author +================== + +Author:: Damian Szeluga () +Author:: Maciej Relewicz () + +Copyright 2016, Mirantis 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. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..0122ee0 --- /dev/null +++ b/Rakefile @@ -0,0 +1,31 @@ +task default: ["test"] + +task :test => [:lint, :style, :unit] + +desc "Vendor the cookbooks in the Berksfile" +task :berks_prep do + sh %{chef exec berks vendor} +end + +desc "Run FoodCritic (lint) tests" +task :lint do + sh %{chef exec foodcritic --epic-fail any --tags ~FC003 --tags ~FC023 .} +end + +desc "Run RuboCop (style) tests" +task :style do + sh %{chef exec rubocop} +end + +desc "Run RSpec (unit) tests" +task :unit => :berks_prep do + sh %{chef exec rspec --format documentation} +end + +desc "Remove the berks-cookbooks directory and the Berksfile.lock" +task :clean do + rm_rf [ + 'berks-cookbooks', + 'Berksfile.lock' + ] +end diff --git a/attributes/default.rb b/attributes/default.rb new file mode 100644 index 0000000..69c4b06 --- /dev/null +++ b/attributes/default.rb @@ -0,0 +1,236 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-application-catalog +# Attributes:: default +# +# Copyright 2016, Mirantis 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. +# + +case node['platform_family'] +when 'rhel' + default['openstack']['murano'] = { + 'api_package_name' => 'openstack-murano-api', + 'cfapi_package_name' => 'openstack-murano-cfapi', + 'common_package_name' => 'openstack-murano-common', + 'engine_package_name' => 'openstack-murano-engine', + 'pythonclient_package_name' => 'openstack-python-muranoclient', + 'dashboard_package_name' => 'openstack-murano-dashboard', + 'api_service_name' => 'murano-api', + 'cfapi_service_name' => 'murano-cfapi', + 'engine_service_name' => 'murano-engine', + 'local_settings_path' => '/etc/openstack-dashboard/local_settings', + 'pymysql_package_name' => nil + } +when 'debian' + default['openstack']['murano'] = { + 'api_package_name' => 'murano-api', + 'cfapi_package_name' => 'murano-cfapi', + 'common_package_name' => 'murano-common', + 'engine_package_name' => 'murano-engine', + 'pythonclient_package_name' => 'python-muranoclient', + 'dashboard_package_name' => 'python-murano-dashboard', + 'api_service_name' => 'murano-api', + 'cfapi_service_name' => 'murano-cfapi', + 'engine_service_name' => 'murano-engine', + 'local_settings_path' => '/etc/openstack-dashboard/local_settings.py', + 'pymysql_package_name' => 'python-pymysql' + } +end + +%w(public internal admin).each do |type| + default['openstack']['endpoints'][type]['application-catalog']['host'] = '127.0.0.1' + default['openstack']['endpoints'][type]['application-catalog']['scheme'] = 'http' + default['openstack']['endpoints'][type]['application-catalog']['port'] = '8082' +end + +default['openstack']['murano']['apt_murano_repo'] = 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/' +default['openstack']['murano']['apt_murano_key'] = 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/archive-mos9.0.key' +default['openstack']['murano']['apt_murano_distribution'] = 'mos9.0' +default['openstack']['murano']['apt_murano_components'] = ['main'] + +default['openstack']['murano']['dbmanage_command'] = 'murano-db-manage --config-file /etc/murano/murano.conf upgrade' +default['openstack']['murano']['default_external_network'] = 'ext-net' +default['openstack']['murano']['tenant'] = 'services' +default['openstack']['murano']['auth_url'] = 'http://127.0.0.1:5000' +default['openstack']['murano']['cfapi_bind_port'] = 8083 +default['openstack']['murano']['cfapi_bind_host'] = '127.0.0.1' +default['openstack']['murano']['engine_workers'] = nil +default['openstack']['murano']['db_name'] = 'murano' +default['openstack']['murano']['db_user'] = 'murano' +default['openstack']['murano']['package_ensure'] = 'present' +default['openstack']['murano']['verbose'] = true +default['openstack']['murano']['debug'] = false +default['openstack']['murano']['use_syslog'] = false +default['openstack']['murano']['use_stderr'] = false +default['openstack']['murano']['log_facility'] = nil +default['openstack']['murano']['log_dir'] = '/var/log/murano' +default['openstack']['murano']['data_dir'] = '/var/cache/murano' +default['openstack']['murano']['notification_driver'] = 'messagingv2' +default['openstack']['murano']['rabbit_ha_queues'] = nil +default['openstack']['murano']['rabbit_own_vhost'] = 'murano' +default['openstack']['murano']['service_host'] = '127.0.0.1' +default['openstack']['murano']['service_port'] = '8082' +default['openstack']['murano']['service_role'] = 'admin' +default['openstack']['murano']['region'] = 'RegionOne' +default['openstack']['murano']['default_log_levels'] = [ + 'amqp=WARN', + 'amqplib=WARN', + 'boto=WARN', + 'iso8601=WARN', + 'keystonemiddleware=WARN', + 'oslo.messaging=INFO', + 'oslo_messaging=INFO', + 'qpid=WARN', + 'requests.packages.urllib3.connectionpool=WARN', + 'requests.packages.urllib3.util.retry=WARN', + 'routes.middleware=WARN', + 'sqlalchemy=WARN', + 'stevedore=WARN', + 'suds=INFO', + 'taskflow=WARN', + 'urllib3.connectionpool=WARN', + 'urllib3.util.retry=WARN', + 'websocket=WARN' +] +default['openstack']['murano']['use_ssl'] = false +default['openstack']['murano']['use_neutron'] = true +default['openstack']['murano']['use_trusts'] = true +default['openstack']['murano']['packages_service'] = 'murano' +default['openstack']['murano']['sync_db'] = true +# v3 +default['openstakc']['murano']['conf']['keystone_authtoken']['auth_type'] = 'v3password' +default['openstack']['murano']['conf']['keystone_authtoken']['project_name'] = 'service' +default['openstack']['murano']['conf']['keystone_authtoken']['project_domain_name'] = 'Default' +default['openstack']['murano']['conf']['keystone_authtoken']['user_domain_name'] = 'Default' +# end of v3 +default['openstack']['murano']['admin_user'] = 'murano' +default['openstack']['murano']['admin_tenant_name'] = 'service' +default['openstack']['murano']['auth_uri'] = 'http://127.0.0.1:5000' +default['openstack']['murano']['identity_uri'] = 'http://127.0.0.1:35357/' +default['openstack']['murano']['signing_dir'] = '/tmp/keystone-signing-muranoapi' +default['openstack']['murano']['cfapi_enabled'] = false +default['openstack']['murano']['dashboard_collect_static_script'] = '/usr/share/openstack-dashboard/manage.py' +default['openstack']['murano']['dashboard_metadata_dir'] = '/var/cache/murano-dashboard' +default['openstack']['murano']['dashboard_max_file_size'] = '5' +default['openstack']['murano']['dashboard_debug_level'] = 'DEBUG' +default['openstack']['murano']['dashboard_client_debug_level'] = 'ERROR' +default['openstack']['murano']['dashboard_sync_db'] = false +default['openstack']['murano']['app_catalog_ui'] = false +default['openstack']['murano']['dashboard_apache_user'] = 'horizon' +default['openstack']['murano']['dashboard_apache_group'] = 'horizon' +default['openstack']['murano']['openrc_path'] = '/root' +default['openstack']['murano']['openrc_path_mode'] = '0700' +default['openstack']['murano']['openrc_file'] = 'muranorc' +default['openstack']['murano']['openrc_file_mode'] = '0600' +default['openstack']['murano']['openrc_user'] = 'root' +default['openstack']['murano']['openrc_group'] = 'root' +default['openstack']['murano']['openrc_repo_url'] = 'http://storage.apps.openstack.org/' +default['openstack']['murano']['cert_file'] = nil +default['openstack']['murano']['key_file'] = nil +default['openstack']['murano']['ca_file'] = nil +default['openstack']['murano']['external_network'] = nil +default['openstack']['murano']['default_router'] = 'murano-default-router' +default['openstack']['murano']['default_nameservers'] = '8.8.8.8' +default['openstack']['murano']['database_connection'] = nil +default['openstack']['murano']['database_idle_timeout'] = nil +default['openstack']['murano']['database_min_pool_size'] = nil +default['openstack']['murano']['database_max_pool_size'] = nil +default['openstack']['murano']['database_max_retries'] = nil +default['openstack']['murano']['database_retry_interval'] = nil +default['openstack']['murano']['database_max_overflow'] = nil +default['openstack']['murano']['dashboard_repo_url'] = nil +default['openstack']['murano']['dashboard_api_url'] = nil +if default['openstack']['murano']['use_ssl'] + unless default['openstack']['murano']['cert_file'] & default['openstack']['murano']['key_file'] & default['openstack']['murano']['ca_file'] + raise 'cert_file, key_file and ca_file parameters must be set when use_ssl is set to true' + end +end +if default['openstack']['murano']['use_neutron'] + unless default['openstack']['murano']['default_router'] + raise 'The default_router parameter is required when use_neutron is set to true' + end +end +default['openstack']['murano']['glare'] = if default['openstack']['murano']['packages_service'] == 'glance' + false + else + true + end +# Config +default['openstack']['murano']['conf'].tap do |conf| + conf['networking']['create_router'] = default['openstack']['murano']['use_neutron'] + conf['networking']['default_dns'] = default['openstack']['murano']['default_nameservers'] + if default['openstack']['murano']['use_neutron'] + conf['networking']['router_name'] = default['openstack']['murano']['default_router'] + conf['networking']['external_network'] = default['openstack']['murano']['external_network'] + end + if default['openstack']['murano']['use_ssl'] + %w(cert_file ca_file key_file).each do |ssl_file| + conf['ssl'][ssl_file] = default['openstack']['murano'][ssl_file] + end + end + conf['murano']['url'] = if default['openstack']['murano']['use_ssl'] + "https://#{default['openstack']['murano']['service_host']}:#{default['openstack']['murano']['service_port']}" + else + "http://#{default['openstack']['murano']['service_host']}:#{default['openstack']['murano']['service_port']}" + end + conf['engine']['use_trusts'] = default['openstack']['murano']['use_trusts'] + conf['rabbitmq']['login'] = default['openstack']['mq']['compute']['rabbit']['userid'] + conf['keystone_authtoken']['auth_uri'] = default['openstack']['murano']['auth_uri'] + conf['keystone_authtoken']['admin_user'] = default['openstack']['murano']['admin_user'] + conf['keystone_authtoken']['admin_tenant_name'] = default['openstack']['murano']['admin_tenant_name'] + conf['keystone_authtoken']['signing_dir'] = default['openstack']['murano']['signing_dir'] + conf['keystone_authtoken']['identity_uri'] = default['openstack']['murano']['identity_uri'] + conf['packages_opts']['packages_service'] = default['openstack']['murano']['packages_service'] + conf['DEFAULT']['notification_driver'] = default['openstack']['murano']['notification_driver'] + conf['DEFAULT']['rabbit_userid'] = default['openstack']['mq']['compute']['rabbit']['userid'] + conf['DEFAULT']['debug'] = default['openstack']['murano']['debug'] + conf['DEFAULT']['verbose'] = default['openstack']['murano']['verbose'] + conf['DEFAULT']['use_stderr'] = default['openstack']['murano']['use_stderr'] + conf['DEFAULT']['use_syslog'] = default['openstack']['murano']['use_syslog'] + conf['DEFAULT']['log_dir'] = default['openstack']['murano']['log_dir'] + if default['openstack']['murano']['use_syslog'] + conf['DEFAULT']['syslog_log_facility'] = default['openstack']['murano']['log_facility'] + end + conf['DEFAULT']['default_log_levels'] = default['openstack']['murano']['default_log_levels'].join(',') + conf['DEFAULT']['bind_host'] = default['openstack']['murano']['service_host'] + conf['DEFAULT']['bind_port'] = default['openstack']['murano']['service_port'] + if default['openstack']['murano']['cfapi_enabled'] + conf['cfapi']['tenant'] = default['openstack']['murano']['tenant'] + conf['cfapi']['auth_uri'] = default['openstack']['murano']['auth_url'] + conf['cfapi']['bind_host'] = default['openstack']['murano']['cfapi_bind_host'] + conf['cfapi']['bind_port'] = default['openstack']['murano']['cfapi_bind_port'] + end + if default['openstack']['murano']['engine_workers'] + conf['engine']['workers'] = default['openstack']['murano']['engine_workers'] + end + if default['openstack']['murano']['database_idle_timeout'] + conf['database']['idle_timeout'] = default['openstack']['murano']['database_idle_timeout'] + end + if default['openstack']['murano']['database_min_pool_size'] + conf['database']['min_pool_size'] = default['openstack']['murano']['database_min_pool_size'] + end + if default['openstack']['murano']['database_max_retries'] + conf['database']['max_retries'] = default['openstack']['murano']['database_max_retries'] + end + if default['openstack']['murano']['database_retry_interval'] + conf['database']['retry_interval'] = default['openstack']['murano']['database_retry_interval'] + end + if default['openstack']['murano']['database_max_pool_size'] + conf['database']['max_pool_size'] = default['openstack']['murano']['database_max_pool_size'] + end + if default['openstack']['murano']['database_max_overflow'] + conf['database']['max_overflow'] = default['openstack']['murano']['database_max_overflow'] + end +end diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..41bbec7 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,6 @@ +#!/bin/bash -x +## This script is for installing all the needed packages on trusty to run the chef tests with 'chef exec rake'. +## It relies on the common bootstrap.sh from openstack/cookbook-openstack-common for installing common dependencies. + +wget -nv -t 3 -O common-bootstrap.sh https://raw.githubusercontent.com/openstack/cookbook-openstack-common/master/bootstrap.sh +/bin/bash -x common-bootstrap.sh diff --git a/chefignore b/chefignore new file mode 100644 index 0000000..38e7379 --- /dev/null +++ b/chefignore @@ -0,0 +1,107 @@ +# Put files/directories that should be ignored in this file when uploading +# to a chef-server or supermarket. +# Lines that start with '# ' are comments. + +# OS generated files # +###################### +.DS_Store +Icon? +nohup.out +ehthumbs.db +Thumbs.db + +# SASS # +######## +.sass-cache + +# EDITORS # +########### +\#* +.#* +*~ +*.sw[a-z] +*.bak +REVISION +TAGS* +tmtags +*_flymake.* +*_flymake +*.tmproj +.project +.settings +mkmf.log + +## COMPILED ## +############## +a.out +*.o +*.pyc +*.so +*.com +*.class +*.dll +*.exe +*/rdoc/ + +# Testing # +########### +.watchr +.rspec +spec/* +spec/fixtures/* +test/* +features/* +examples/* +Guardfile +Procfile +.kitchen* +.rubocop.yml +spec/* +Rakefile +.travis.yml +.foodcritic +.codeclimate.yml + +# SCM # +####### +.git +*/.git +.gitignore +.gitmodules +.gitconfig +.gitattributes +.svn +*/.bzr/* +*/.hg/* +*/.svn/* + +# Berkshelf # +############# +Berksfile +Berksfile.lock +cookbooks/* +tmp + +# Policyfile # +############## +Policyfile.rb +Policyfile.lock.json + +# Cookbooks # +############# +CONTRIBUTING* +CHANGELOG* +TESTING* +MAINTAINERS.toml + +# Strainer # +############ +Colanderfile +Strainerfile +.colander +.strainer + +# Vagrant # +########### +.vagrant +Vagrantfile diff --git a/libraries/matchers.rb b/libraries/matchers.rb new file mode 100644 index 0000000..6a1644a --- /dev/null +++ b/libraries/matchers.rb @@ -0,0 +1,9 @@ +# encoding: UTF-8 +if defined?(ChefSpec) + def create_openstack_application_catalog_application(resource_name) + ChefSpec::Matchers::ResourceMatcher.new( + :openstack_application_catalog_application, + :create, + resource_name) + end +end diff --git a/metadata.rb b/metadata.rb new file mode 100644 index 0000000..62d0a3c --- /dev/null +++ b/metadata.rb @@ -0,0 +1,17 @@ +name 'openstack-application-catalog' +maintainer 'openstack-chef' +maintainer_email 'openstack-dev@lists.openstack.org' +issues_url 'https://launchpad.net/openstack-chef' if respond_to?(:issues_url) +source_url 'https://github.com/openstack/cookbook-openstack-application-catalog' if respond_to?(:source_url) +license 'Apache 2.0' +description 'Installs/Configures openstack-application-catalog' +long_description 'Installs/Configures openstack-application-catalog' +version '0.1.0' + +supports 'ubuntu' + +depends 'openstack-common', '>= 14.0.0' +depends 'openstack-identity', '>= 14.0.0' +depends 'openstack-ops-messaging', '>= 14.0.0' +depends 'openstackclient' +depends 'apache2' diff --git a/providers/application.rb b/providers/application.rb new file mode 100644 index 0000000..a5369d4 --- /dev/null +++ b/providers/application.rb @@ -0,0 +1,57 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-application-catalog +# Provider:: application +# +# Copyright 2016, Mirantis 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. +# + +include ::Openstack + +use_inline_resources + +action :create do + @user = new_resource.identity_user + @pass = new_resource.identity_pass + @tenant = new_resource.identity_tenant + @ks_uri = new_resource.identity_uri + + name = new_resource.name + package_path = "/var/cache/murano/meta/#{name}.zip" + category = new_resource.category ? new_resource.category : nil + is_public = new_resource.is_public + + ep = public_endpoint 'application-catalog' + @api = ep.to_s.gsub(ep.path, '') # remove trailing /v2 + + _add_application(name, package_path, category, is_public) + new_resource.updated_by_last_action(true) +end + +action :destroy do + # TODO +end + +private + +def _add_application(name, package_path, category, is_public) + murano_cmd = "murano --insecure --os-username #{@user} --os-password #{@pass} --os-tenant-name #{@tenant} --murano-url #{@api} --os-auth-url #{@ks_uri}" + murano_opts = '' + murano_opts += '--is-public' if is_public + murano_opts += "-C #{category}" if category + execute "Adding #{name} application" do + command "#{murano_cmd} package-import #{murano_opts} #{package_path}" + end +end diff --git a/recipes/dashboard.rb b/recipes/dashboard.rb new file mode 100644 index 0000000..b550062 --- /dev/null +++ b/recipes/dashboard.rb @@ -0,0 +1,80 @@ +# Cookbook Name:: openstack-application-catalog +# Recipe:: dashboard +# +# Copyright (c) 2016 Mirantis Inc, All Rights Reserved. +# +# 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 'uri' +class ::Chef::Recipe + include ::Openstack +end + +if node['openstack']['murano']['glare'] + package 'murano-glance-artifacts-plugin' do + action :install + end +end + +if node['openstack']['murano']['app_catalog_ui'] + package 'python-app-catalog-ui' do + action :install + end +end + +package 'python-murano-dashboard' + +include_recipe 'apache2' + +template node['openstack']['murano']['local_settings_path'] do + source 'local_settings.py.erb' + mode 00440 + owner 'root' + group 'root' + variables( + api_url: node['openstack']['murano']['dashboard_api_url'], + repo_url: node['openstack']['murano']['dashboard_repo_url'], + max_file_size: node['openstack']['murano']['dashboard_max_file_size'], + metadata_dir: node['openstack']['murano']['dashboard_metadata_dir'], + dashboard_debug_level: node['openstack']['murano']['dashboard_debug_level'], + client_debug_level: node['openstack']['murano']['dashboard_client_debug_level'], + enable_glare: node['openstack']['murano']['glare'], + compress_offline: true + ) +end + +dashboard_user = node['openstack']['murano']['dashboard_apache_user'] +dashboard_group = node['openstack']['murano']['dashboard_apache_group'] + +execute 'clean_horizon_config' do + command "sed -e '/^## MURANO_CONFIG_BEGIN/,/^## MURANO_CONFIG_END ##/ d' -i #{node['openstack']['murano']['local_settings_path']}" + only_if "grep '^## MURANO_CONFIG_BEGIN' #{node['openstack']['murano']['local_settings_path']}" +end + +execute 'django_collectstatic' do + command "#{node['openstack']['murano']['dashboard_collect_static_script']} collectstatic --noinput" + environment ({ + 'APACHE_USER' => dashboard_user, + 'APACHE_GROUP' => dashboard_group + }) +end + +execute 'django_compressstatic' do + command "#{node['openstack']['murano']['dashboard_collect_static_script']} compress --force" + environment ({ + 'APACHE_USER' => dashboard_user, + 'APACHE_GROUP' => dashboard_group + }) + notifies :reload, 'service[apache2]', :immediately +end diff --git a/recipes/default.rb b/recipes/default.rb new file mode 100644 index 0000000..ddf23a3 --- /dev/null +++ b/recipes/default.rb @@ -0,0 +1,17 @@ +# Cookbook Name:: openstack-application-catalog +# Recipe:: default +# +# Copyright (c) 2016 Mirantis Inc, All Rights Reserved. +# +# 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. +# diff --git a/recipes/openrc.rb b/recipes/openrc.rb new file mode 100644 index 0000000..f5e55aa --- /dev/null +++ b/recipes/openrc.rb @@ -0,0 +1,36 @@ +# Cookbook Name:: openstack-application-catalog +# Recipe:: openrc +# +# Copyright (c) 2016 Mirantis Inc, All Rights Reserved. +# +# 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 'uri' +class ::Chef::Recipe + include ::Openstack +end + +openrc_file = "#{node['openstack']['murano']['openrc_path']}/#{node['openstack']['murano']['openrc_file']}" + +template openrc_file do + source 'openrc.erb' + owner node['openstack']['murano']['openrc_user'] + group node['openstack']['murano']['openrc_group'] + mode node['openstack']['murano']['openrc_file_mode'] + sensitive true + variables hash: { + openrc_repo_url: node['openstack']['murano']['openrc_repo_url'], + enable_glare: node['openstack']['murano']['glare'] + } +end diff --git a/recipes/server.rb b/recipes/server.rb new file mode 100644 index 0000000..31636c8 --- /dev/null +++ b/recipes/server.rb @@ -0,0 +1,404 @@ +# Cookbook Name:: openstack-application-catalog +# Recipe:: server +# +# Copyright (c) 2016 Mirantis Inc, All Rights Reserved. +# +# 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 'uri' +class ::Chef::Recipe + include ::Openstack +end + +apt_repository 'murano_repo' do + uri node['openstack']['murano']['apt_murano_repo'] + distribution node['openstack']['murano']['apt_murano_distribution'] + components node['openstack']['murano']['apt_murano_components'] + key node['openstack']['murano']['apt_murano_key'] + action :add +end + +# temporary until CR-25599 is published +apt_repository 'murano_temp_repo' do + uri 'http://perestroika-repo-tst.infra.mirantis.net/mos-repos/ubuntu/9.0/' + distribution 'mos9.0-proposed' + components node['openstack']['murano']['apt_murano_components'] + key node['openstack']['murano']['apt_murano_key'] + action :add +end + +apt_preference 'pin_murano_repo_other_temp' do + glob '*' + pin 'origin perestroika-repo-tst.infra.mirantis.net' + pin_priority '-1' +end + +apt_preference 'pin_murano_repo_temp' do + glob 'python-murano-dashboard' + pin 'origin perestroika-repo-tst.infra.mirantis.net' + pin_priority '1200' +end +# end of temporary fix + +packages = %w(api_package_name cfapi_package_name common_package_name + engine_package_name pythonclient_package_name) + +if node['openstack']['murano']['database_connection'] =~ /^mysql\+pymysql/ + packages << 'pymysql_package_name' +end + +apt_preference 'pin_murano_repo' do + glob 'murano-glance-artifacts-plugin python-django-formtools python-django-babel murano-api murano-cfapi murano-common murano-engine python-murano python-muranoclient init-system-helpers' + pin 'origin mirror.fuel-infra.org' + pin_priority '1200' +end + +apt_preference 'pin_murano_repo_other' do + glob '*' + pin 'origin mirror.fuel-infra.org' + pin_priority '-1' +end + +packages.each do |pkg| + package node['openstack']['murano'][pkg] do + action :install + end +end + +# config + +db_password = get_password 'db', 'murano' +db_user = node['openstack']['murano']['db_user'] +db_name = node['openstack']['murano']['db_name'] +db_connection = db_uri('application-catalog', db_user, db_password) + +user = node['openstack']['mq']['network']['rabbit']['userid'] +node.default['openstack']['murano']['conf_secrets'] +.[]('DEFAULT')['rabbit_password'] = + get_password 'user', user +node.default['openstack']['murano']['conf_secrets'] +.[]('rabbitmq')['password'] = + get_password 'user', user +node.default['openstack']['murano']['conf_secrets'] +.[]('keystone_authtoken')['admin_password'] = + get_password 'user', 'admin' + +node.default['openstack']['murano']['conf'] +.[]('database')['connection'] = db_connection + +murano_conf_options = merge_config_options 'murano' + +directory '/etc/murano' do + mode 00755 + action :create +end + +directory '/var/log/murano' do + mode 00755 + owner 'murano' + group 'murano' + action :create +end + +template '/etc/murano/murano.conf' do + source 'openstack-service.conf.erb' + cookbook 'openstack-common' + owner 'murano' + group 'murano' + mode 00640 + variables( + service_config: murano_conf_options + ) +end + +file '/etc/murano/murano-paste.ini' do + mode 00644 +end + +file '/etc/murano/policy.json' do + mode 00644 +end + +service 'murano-api' do + supports restart: true, reload: true + action :enable +end + +service 'murano-cfapi' do + supports restart: true, reload: true + action :enable +end + +service 'murano-engine' do + supports restart: true, reload: true + action :enable +end + +bind_db = node['openstack']['bind_service']['db'] +if bind_db['interface'] + listen_address = address_for bind_db['interface'] +else + listen_address = bind_db['host'] +end + +super_password = get_password 'db', node['openstack']['db']['root_user_key'] + +if node['openstack']['db']['service_type'] == 'mysql' + + # Create a sql server database + mysql_database db_name do + connection( + host: listen_address, + port: bind_db.port.to_s, + username: 'root', + password: super_password, + options: { 'CHARSET' => 'utf8', 'COLLATE' => 'utf8_general_ci' } + ) + action :create + end + + # Create user + mysql_database_user db_user do + connection( + host: listen_address, + username: 'root', + password: super_password + ) + password db_password + database_name db_name + privileges [:all] + action :grant + host '%' + end + +end + +if node['openstack']['db']['service_type'] == 'postgresql' + # postgresql database backend has not been tested. + # patches are welcome + + postgresql_connection_info = { + host: listen_address, + port: node['postgresql']['config']['port'], + username: 'postgres', + password: node['postgresql']['password']['postgres'] + } + + # Create user + postgresql_database_user db_user do + connection postgresql_connection_info + password super_password + action :create + end + + # Create a postgress server database + postgresql_database db_name do + connection( + host: listen_addres, + port: bind_db.port.to_s, + username: 'root', + password: super_password + ) + action :create + end + + postgresql_database_user db_user do + connection postgresql_connection_info + database_name db_name + privileges [:all] + action :grant + end +end + +include_recipe 'openstack-application-catalog::dashboard' + +execute 'murano-dbmanage' do + command node['openstack']['murano']['dbmanage_command'] + user 'murano' + notifies :restart, 'service[murano-api]', :immediately + notifies :restart, 'service[murano-cfapi]', :immediately + notifies :restart, 'service[murano-engine]', :immediately +end + +rabbitmq_user 'remove rabbit guest user' do + user 'guest' + action :delete + not_if { user == 'guest' } +end + +rabbitmq_user 'add murano rabbit user' do + user node['openstack']['mq']['compute']['rabbit']['userid'] + password node['openstack']['murano']['conf_secrets']['rabbitmq']['password'] + action :add +end + +rabbitmq_user 'change murano rabbit user password' do + user node['openstack']['mq']['compute']['rabbit']['userid'] + password node['openstack']['murano']['conf_secrets']['rabbitmq']['password'] + action :change_password +end + +rabbitmq_vhost 'add murano rabbit vhost' do + vhost node['openstack']['murano']['rabbit_own_vhost'] + action :add +end + +rabbitmq_user 'set murano user permissions' do + user node['openstack']['mq']['compute']['rabbit']['userid'] + vhost node['openstack']['murano']['rabbit_own_vhost'] + permissions '.* .* .*' + action :set_permissions +end + +ie = public_endpoint 'identity' +ae = admin_endpoint 'identity' +ine = internal_endpoint 'identity' +auth_uri = ::URI.decode ae.to_s + +murano_port = node['openstack']['murano']['service_port'] + +public_murano_api_endpoint = "#{ie.scheme}://#{ie.host}:#{murano_port}/" +admin_murano_api_endpoint = "#{ae.scheme}://#{ae.host}:#{murano_port}/" +internal_murano_api_endpoint = "#{ine.scheme}://#{ine.host}:#{murano_port}/" + +service_pass = get_password 'service', 'openstack-application-catalog' +service_project_name = node['openstack']['murano']['conf']['keystone_authtoken']['project_name'] +service_domain_name = node['openstack']['murano']['conf']['keystone_authtoken']['user_domain_name'] +admin_user = node['openstack']['identity']['admin_user'] +admin_pass = get_password 'user', node['openstack']['identity']['admin_user'] +admin_project = node['openstack']['identity']['admin_project'] +admin_domain = node['openstack']['identity']['admin_domain_name'] +service_user = node['openstack']['murano']['admin_user'] +service_role = node['openstack']['murano']['service_role'] +region = node['openstack']['region'] +murano_service_name = 'murano' +murano_service_type = 'application-catalog' +cfapi_service_name = 'murano-cfapi' +cfapi_service_type = 'service-broker' + +connection_params = { + openstack_auth_url: "#{auth_uri}/auth/tokens", + openstack_username: admin_user, + openstack_api_key: admin_pass, + openstack_project_name: admin_project, + openstack_domain_name: admin_domain +} + +# Register Service Tenant +openstack_project service_project_name do + connection_params connection_params +end + +# Register Murano Service +openstack_service murano_service_name do + type murano_service_type + connection_params connection_params +end + +# Register Murano Public-Endpoint +openstack_endpoint murano_service_type do + service_name murano_service_name + interface 'public' + url ::URI.decode public_murano_api_endpoint.to_s + region region + connection_params connection_params +end + +# Register Murano Internal-Endpoint +openstack_endpoint murano_service_type do + service_name murano_service_name + url ::URI.decode internal_murano_api_endpoint.to_s + region region + connection_params connection_params +end + +# Register Murano Admin-Endpoint +openstack_endpoint murano_service_type do + service_name murano_service_name + interface 'admin' + url ::URI.decode admin_murano_api_endpoint.to_s + region region + connection_params connection_params +end + +# Register Service User +openstack_user service_user do + project_name service_project_name + role_name service_role + password service_pass + connection_params connection_params +end + +## Grant Service role to Service User for Service Tenant ## +openstack_user service_user do + role_name service_role + project_name service_project_name + connection_params connection_params + action :grant_role +end + +openstack_user service_user do + domain_name service_domain_name + role_name service_role + user_name service_user + connection_params connection_params + action :grant_domain +end + +if node['openstack']['murano']['cfapi_enabled'] + public_cfapi_api_endpoint = "#{ie.scheme}://#{ie.host}:8083/" + admin_cfapi_api_endpoint = "#{ae.scheme}://#{ae.host}:8083/" + internal_cfapi_api_endpoint = "#{ine.scheme}://#{ine.host}:8083/" + + # Register CFAPI Service + openstack_service cfapi_service_name do + type cfapi_service_type + connection_params connection_params + end + + # Register CFAPI Public-Endpoint + openstack_endpoint cfapi_service_type do + service_name cfapi_service_name + interface 'public' + url ::URI.decode public_cfapi_api_endpoint.to_s + region region + connection_params connection_params + end + + # Register CFAPI Internal-Endpoint + openstack_endpoint cfapi_service_type do + service_name cfapi_service_name + url ::URI.decode internal_cfapi_api_endpoint.to_s + region region + connection_params connection_params + end + + # Register CFAPI Admin-Endpoint + openstack_endpoint cfapi_service_type do + service_name cfapi_service_name + interface 'admin' + url ::URI.decode admin_cfapi_api_endpoint.to_s + region region + connection_params connection_params + end +end + +openstack_application_catalog_application 'io.murano' do + identity_user node['openstack']['murano']['admin_user'] + identity_pass service_pass + identity_tenant service_project_name + identity_uri auth_uri + is_public true + action :create +end diff --git a/resources/application.rb b/resources/application.rb new file mode 100644 index 0000000..6244cf2 --- /dev/null +++ b/resources/application.rb @@ -0,0 +1,36 @@ +# encoding: UTF-8 +# +# Cookbook Name:: openstack-application-catalog +# Resource:: application +# +# Copyright 2016, Mirantis 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. +# + +actions :create, :destroy + +# In earlier versions of Chef the LWRP DSL doesn't support specifying +# a default action, so you need to drop into Ruby. +def initialize(*args) + super + @action = :create +end + +attribute :name, kind_of: String +attribute :category, kind_of: String +attribute :identity_user, kind_of: String +attribute :identity_pass, kind_of: String +attribute :identity_tenant, kind_of: String +attribute :identity_uri, kind_of: String +attribute :is_public, kind_of: [TrueClass, FalseClass], default: false diff --git a/spec/apt_spec.rb b/spec/apt_spec.rb new file mode 100644 index 0000000..9192f62 --- /dev/null +++ b/spec/apt_spec.rb @@ -0,0 +1,61 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + it 'configures murano repository' do + expect(chef_run).to add_apt_repository('murano_repo').with( + key: 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/archive-mos9.0.key', + uri: 'http://mirror.fuel-infra.org/mos-repos/ubuntu/9.0/', + distribution: 'mos9.0', + components: ['main']) + end + + it 'installs the murano-api package' do + expect(chef_run).to install_package 'murano-api' + end + + it 'installs the murano-cfapi package' do + expect(chef_run).to install_package 'murano-cfapi' + end + + it 'installs the murano-common package' do + expect(chef_run).to install_package 'murano-common' + end + + it 'installs the murano-engine package' do + expect(chef_run).to install_package 'murano-engine' + end + + it 'installs the python-muranoclient package' do + expect(chef_run).to install_package 'python-muranoclient' + end + + it 'installs the murano-glance-artifacts-plugin package' do + expect(chef_run).to install_package 'murano-glance-artifacts-plugin' + end + + it 'installs the python-murano-dashboard package' do + expect(chef_run).to install_package 'python-murano-dashboard' + end + + it 'enables the murano-api service' do + expect(chef_run).to enable_service('murano-api') + end + + it 'enables the murano-cfapi service' do + expect(chef_run).to enable_service('murano-cfapi') + end + + it 'enables the murano-engine service' do + expect(chef_run).to enable_service('murano-engine') + end + end +end diff --git a/spec/dashboard_spec.rb b/spec/dashboard_spec.rb new file mode 100644 index 0000000..c931a90 --- /dev/null +++ b/spec/dashboard_spec.rb @@ -0,0 +1,50 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::dashboard' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + describe '/etc/openstack-dashboard/local_settings.py' do + let(:file) { chef_run.template('/etc/openstack-dashboard/local_settings.py') } + + it 'creates local_settings.py' do + expect(chef_run).to create_template(file.name).with( + user: 'root', + group: 'root', + mode: 00440 + ) + end + end + + describe 'openstack-dashboard' do + cmd = '/usr/share/openstack-dashboard/manage.py' + cmd_environment = { + 'APACHE_USER' => 'horizon', + 'APACHE_GROUP' => 'horizon' + } + clean_cmd = "sed -e '/^## MURANO_CONFIG_BEGIN/,/^## MURANO_CONFIG_END ##/ d' -i /etc/openstack-dashboard/local_settings.py" + + it 'cleans horizon config' do + expect(chef_run).to run_execute(clean_cmd) + end + + it 'runs collectstatic' do + expect(chef_run).to run_execute("#{cmd} collectstatic --noinput").with( + environment: cmd_environment + ) + end + + it 'runs compress' do + expect(chef_run).to run_execute("#{cmd} compress --force").with( + environment: cmd_environment + ) + end + end + end +end diff --git a/spec/db_spec.rb b/spec/db_spec.rb new file mode 100644 index 0000000..92c035d --- /dev/null +++ b/spec/db_spec.rb @@ -0,0 +1,21 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + it 'create mysql database' do + expect(chef_run).to create_mysql_database('murano') + end + + it 'create mysql user' do + expect(chef_run).to grant_mysql_database_user('murano') + end + end +end diff --git a/spec/identity_registration_spec.rb b/spec/identity_registration_spec.rb new file mode 100644 index 0000000..ee5b505 --- /dev/null +++ b/spec/identity_registration_spec.rb @@ -0,0 +1,111 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + connection_params = { + openstack_auth_url: 'http://127.0.0.1:35357/v3/auth/tokens', + openstack_username: 'admin', + openstack_api_key: 'admin-pass', + openstack_project_name: 'admin', + openstack_domain_name: 'default' + } + service_name = 'murano' + service_type = 'application-catalog' + service_user = 'murano' + url = 'http://127.0.0.1:8082/' + region = 'RegionOne' + project_name = 'service' + role_name = 'admin' + password = 'murano-pass' + domain_name = 'Default' + + it "registers #{project_name} Project" do + expect(chef_run).to create_openstack_project(project_name).with( + connection_params: connection_params + ) + end + + it "registers #{service_name} service" do + expect(chef_run).to create_openstack_service(service_name).with( + connection_params: connection_params, + type: service_type + ) + end + + context "registers #{service_name} endpoint" do + %w(admin internal public).each do |interface| + it "#{interface} endpoint with default values" do + expect(chef_run).to create_openstack_endpoint( + service_type + ).with( + service_name: service_name, + url: url, + region: region, + connection_params: connection_params + ) + end + end + end + + it 'registers service user' do + expect(chef_run).to create_openstack_user( + service_user + ).with( + project_name: project_name, + role_name: role_name, + password: password, + connection_params: connection_params + ) + end + + it do + expect(chef_run).to grant_domain_openstack_user( + service_user + ).with( + domain_name: domain_name, + role_name: role_name, + connection_params: connection_params + ) + end + + it do + expect(chef_run).to grant_role_openstack_user( + service_user + ).with( + project_name: project_name, + role_name: role_name, + password: password, + connection_params: connection_params + ) + end + + it 'register murano service' do + expect(chef_run).to create_openstack_service( + 'murano' + ).with( + connection_params: connection_params + ) + end + + %w(admin internal public).each do |interface| + it "#{interface} murano endpoint with default values" do + expect(chef_run).to create_openstack_endpoint( + 'application-catalog' + ).with( + service_name: 'murano', + url: 'http://127.0.0.1:8082/', + region: region, + connection_params: connection_params + ) + end + end + end +end diff --git a/spec/mq_spec.rb b/spec/mq_spec.rb new file mode 100644 index 0000000..36824d0 --- /dev/null +++ b/spec/mq_spec.rb @@ -0,0 +1,50 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + context 'custom mq attributes' do + before do + node.set['openstack']['mq']['compute']['rabbit']['userid'] = 'not-a-guest' + node.set['openstack']['murano']['rabbit_own_vhost'] = '/foo' + end + + it 'does not delete guest user' do + expect(chef_run).not_to delete_rabbitmq_user( + 'remove rabbit guest user' + ).with(user: 'guest') + end + + it 'adds murano rabbit user' do + expect(chef_run).to add_rabbitmq_user( + 'add murano rabbit user' + ).with(user: 'not-a-guest', password: 'mq-pass') + end + + it 'changes murano rabbit user password' do + expect(chef_run).to change_password_rabbitmq_user( + 'change murano rabbit user password' + ).with(user: 'not-a-guest', password: 'mq-pass') + end + + it 'adds murano rabbit vhost' do + expect(chef_run).to add_rabbitmq_vhost( + 'add murano rabbit vhost' + ).with(vhost: '/foo') + end + + it 'sets murano user permissions' do + expect(chef_run).to set_permissions_rabbitmq_user( + 'set murano user permissions' + ).with(user: 'not-a-guest', vhost: '/foo', permissions: '.* .* .*') + end + end + end +end diff --git a/spec/server_spec.rb b/spec/server_spec.rb new file mode 100644 index 0000000..fc013ad --- /dev/null +++ b/spec/server_spec.rb @@ -0,0 +1,30 @@ +# encoding: UTF-8 + +require_relative 'spec_helper' + +describe 'openstack-application-catalog::server' do + describe 'ubuntu' do + let(:runner) { ChefSpec::SoloRunner.new(UBUNTU_OPTS.merge(step_into: ['openstack_application_catalog_application'])) } + let(:node) { runner.node } + let(:chef_run) { runner.converge(described_recipe) } + + include_context 'application-catalog-stubs' + + describe 'server' do + cmd = 'murano-db-manage --config-file /etc/murano/murano.conf upgrade' + it 'runs dbmanage' do + expect(chef_run).to run_execute(cmd).with( + user: 'murano' + ) + end + + it 'imports io.murano package' do + expect(chef_run).to create_openstack_application_catalog_application('io.murano') + end + + it 'adds io.murano application' do + expect(chef_run).to run_execute('Adding io.murano application') + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..8386b53 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,48 @@ +# encoding: UTF-8 +require 'chefspec' +require 'chefspec/berkshelf' + +ChefSpec::Coverage.start! { add_filter 'openstack-application-catalog::server' } + +require 'chef/application' + +UBUNTU_OPTS = { + platform: 'ubuntu', + version: '16.04', + log_level: :fatal +}.freeze + +shared_context 'application-catalog-stubs' do + before do + stub_command('/usr/sbin/apache2 -t').and_return(true) + stub_command("grep '^## MURANO_CONFIG_BEGIN' /etc/openstack-dashboard/local_settings.py").and_return(true) + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('db', 'murano') + .and_return('murano-dbpass') + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('service', 'openstack-application-catalog') + .and_return('murano-pass') + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('user', 'guest') + .and_return('mq-pass') + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('user', 'admin') + .and_return('admin-pass') + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('token', 'openstack_identity_bootstrap_token') + .and_return('bootstrap-token') + allow_any_instance_of(Chef::Recipe).to receive(:get_password) + .with('db', 'mysqlroot') + .and_return('mysqlroot') + allow(Chef::Application).to receive(:fatal!) + end +end + +shared_examples 'expect-runs-recipe' do + it 'runs server recipe' do + expect(chef_run).to include_recipe 'openstack-application-catalog::server' + end + it 'runs dashboard recipe' do + expect(chef_run).to include_recipe 'openstack-application-catalog::dashboard' + end +end diff --git a/templates/default/local_settings.py.erb b/templates/default/local_settings.py.erb new file mode 100644 index 0000000..fb19533 --- /dev/null +++ b/templates/default/local_settings.py.erb @@ -0,0 +1,566 @@ + +# This file autogenerated by Chef +# Do not edit, changes will be overwritten + + +import os + +from django.utils.translation import ugettext_lazy as _ + +from openstack_dashboard import exceptions + +DEBUG = False +TEMPLATE_DEBUG = DEBUG + +WEBROOT = '/' + + +# Required for Django 1.5. +# If horizon is running in production (DEBUG is False), set this +# with the list of host/domain names that the application can serve. +# For more information see: +# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts +ALLOWED_HOSTS = ["*"] + + +# If Horizon is being served through SSL, then uncomment the following two +# settings to better secure the cookies from security exploits +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SECURE = True + +# Overrides for OpenStack API versions. Use this setting to force the +# OpenStack dashboard to use a specfic API version for a given service API. +# NOTE: The version should be formatted as it appears in the URL for the +# service API. For example, The identity service APIs have inconsistent +# use of the decimal point, so valid options would be "2.0" or "3". +OPENSTACK_API_VERSIONS = { + "identity": 2.0, + "volume": 2 +} + +# Set this to True if running on multi-domain model. When this is enabled, it +# will require user to enter the Domain name in addition to username for login. +OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = False + +# Overrides the default domain used when running on single-domain model +# with Keystone V3. All entities will be created in the default domain. + +# Set Console type: +# valid options would be "AUTO", "VNC", "SPICE" or "RDP" +CONSOLE_TYPE = "AUTO" + +# Default OpenStack Dashboard configuration. +HORIZON_CONFIG = { + 'user_home': 'openstack_dashboard.views.get_user_home', + 'ajax_queue_limit': 10, + 'auto_fade_alerts': { + 'delay': 3000, + 'fade_duration': 1500, + 'types': ['alert-success', 'alert-info'] + }, + 'help_url': "http://docs.openstack.org", + 'exceptions': {'recoverable': exceptions.RECOVERABLE, + 'not_found': exceptions.NOT_FOUND, + 'unauthorized': exceptions.UNAUTHORIZED}, + 'angular_modules': [], + 'js_files': [], +} + +# Specify a regular expression to validate user passwords. +# HORIZON_CONFIG["password_validator"] = { +# "regex": '.*', +# "help_text": _("Your password does not meet the requirements.") +# } + +# Disable simplified floating IP address management for deployments with +# multiple floating IP pools or complex network requirements. +HORIZON_CONFIG["simple_ip_management"] = False + +# Turn off browser autocompletion for forms including the login form and +# the database creation workflow if so desired. +HORIZON_CONFIG["password_autocomplete"] = "off" + +LOCAL_PATH = os.path.dirname(os.path.abspath(__file__)) + +# Set custom secret key: +# You can either set it to a specific value or you can let horizion generate a +# default secret key that is unique on this machine, e.i. regardless of the +# amount of Python WSGI workers (if used behind Apache+mod_wsgi): However, there +# may be situations where you would want to set this explicitly, e.g. when +# multiple dashboard instances are distributed on different machines (usually +# behind a load-balancer). Either you have to make sure that a session gets all +# requests routed to the same dashboard instance or you set the same SECRET_KEY +# for all of them. +from horizon.utils import secret_key +SECRET_KEY = secret_key.generate_or_read_from_file(os.path.realpath('/var/lib/openstack-dashboard/secret_key')) + +# We recommend you use memcached for development; otherwise after every reload +# of the django development server, you will have to login again. To use +# memcached set CACHE_BACKED to something like 'memcached://127.0.0.1:11211/' + +# Send email to the console by default +EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' +# Or send them to /dev/null +#EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' + +# Configure these for your outgoing email host +# EMAIL_HOST = 'smtp.my-company.com' +# EMAIL_PORT = 25 +# EMAIL_HOST_USER = 'djangomail' +# EMAIL_HOST_PASSWORD = 'top-secret!' + +# For multiple regions uncomment this configuration, and add (endpoint, title). +# AVAILABLE_REGIONS = [ +# ('http://cluster1.example.com:5000/v2.0', 'cluster1'), +# ('http://cluster2.example.com:5000/v2.0', 'cluster2'), +# ] + +OPENSTACK_KEYSTONE_URL = "http://127.0.0.1:5000/v2.0" +OPENSTACK_KEYSTONE_ADMIN_URL = "http://127.0.0.1:35357/v2.0" +OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_" + +# Disable SSL certificate checks (useful for self-signed certificates): +# OPENSTACK_SSL_NO_VERIFY = True +OPENSTACK_SSL_NO_VERIFY = True + +# The CA certificate to use to verify SSL connections +# OPENSTACK_SSL_CACERT = '/path/to/cacert.pem' + +# The OPENSTACK_KEYSTONE_BACKEND settings can be used to identify the +# capabilities of the auth backend for Keystone. +# If Keystone has been configured to use LDAP as the auth backend then set +# can_edit_user to False and name to 'ldap'. +# +# TODO(tres): Remove these once Keystone has an API to identify auth backend. +OPENSTACK_KEYSTONE_BACKEND = { + 'name': 'native', + 'can_edit_user': True, + 'can_edit_group': True, + 'can_edit_project': True, + 'can_edit_domain': True, + 'can_edit_role': True, +} + +#Setting this to True, will add a new "Retrieve Password" action on instance, +#allowing Admin session password retrieval/decryption. +#OPENSTACK_ENABLE_PASSWORD_RETRIEVE = False + +# The Xen Hypervisor has the ability to set the mount point for volumes +# attached to instances (other Hypervisors currently do not). Setting +# can_set_mount_point to True will add the option to set the mount point +# from the UI. +OPENSTACK_HYPERVISOR_FEATURES = { + 'can_set_mount_point': True, + 'can_set_password': False, +} + +# The OPENSTACK_CINDER_FEATURES settings can be used to enable optional +# services provided by cinder that is not exposed by its extension API. +OPENSTACK_CINDER_FEATURES = { + 'enable_backup': False, +} + +# The OPENSTACK_NEUTRON_NETWORK settings can be used to enable optional +# services provided by neutron. Options currently available are load +# balancer service, security groups, quotas, VPN service. +OPENSTACK_NEUTRON_NETWORK = { + 'enable_lb': False, + 'enable_quotas': True, + # The profile_support option is used to detect if an external router can be + # configured via the dashboard. When using specific plugins the + # profile_support can be turned on if needed. + 'profile_support': None, + #'profile_support': 'cisco', + # Set which provider network types are supported. Only the network types + # in this list will be available to choose from when creating a network. + # Network types include local, flat, vlan, gre, and vxlan. + 'supported_provider_types': ['*'], +} + +# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features +# in the OpenStack Dashboard related to the Image service, such as the list +# of supported image formats. +# OPENSTACK_IMAGE_BACKEND = { +# 'image_formats': [ +# ('', _('Select format')), +# ('aki', _('AKI - Amazon Kernel Image')), +# ('ami', _('AMI - Amazon Machine Image')), +# ('ari', _('ARI - Amazon Ramdisk Image')), +# ('iso', _('ISO - Optical Disk Image')), +# ('qcow2', _('QCOW2 - QEMU Emulator')), +# ('raw', _('Raw')), +# ('vdi', _('VDI')), +# ('vhd', _('VHD')), +# ('vmdk', _('VMDK')) +# ] +# } + +# The IMAGE_CUSTOM_PROPERTY_TITLES settings is used to customize the titles for +# image custom property attributes that appear on image detail pages. +IMAGE_CUSTOM_PROPERTY_TITLES = { + "architecture": _("Architecture"), + "kernel_id": _("Kernel ID"), + "ramdisk_id": _("Ramdisk ID"), + "image_state": _("Euca2ools state"), + "project_id": _("Project ID"), + "image_type": _("Image Type") +} + +# The IMAGE_RESERVED_CUSTOM_PROPERTIES setting is used to specify which image +# custom properties should not be displayed in the Image Custom Properties +# table. +IMAGE_RESERVED_CUSTOM_PROPERTIES = [] + +# OPENSTACK_ENDPOINT_TYPE specifies the endpoint type to use for the endpoints +# in the Keystone service catalog. Use this setting when Horizon is running +# external to the OpenStack environment. The default is 'publicURL'. +#OPENSTACK_ENDPOINT_TYPE = "publicURL" + +# SECONDARY_ENDPOINT_TYPE specifies the fallback endpoint type to use in the +# case that OPENSTACK_ENDPOINT_TYPE is not present in the endpoints +# in the Keystone service catalog. Use this setting when Horizon is running +# external to the OpenStack environment. The default is None. This +# value should differ from OPENSTACK_ENDPOINT_TYPE if used. +#SECONDARY_ENDPOINT_TYPE = "publicURL" + +# The number of objects (Swift containers/objects or images) to display +# on a single page before providing a paging element (a "more" link) +# to paginate results. +API_RESULT_LIMIT = 1000 +API_RESULT_PAGE_SIZE = 20 + +# The timezone of the server. This should correspond with the timezone +# of your entire OpenStack installation, and hopefully be in UTC. +TIME_ZONE = "UTC" + +# When launching an instance, the menu of available flavors is +# sorted by RAM usage, ascending. If you would like a different sort order, +# you can provide another flavor attribute as sorting key. Alternatively, you +# can provide a custom callback method to use for sorting. You can also provide +# a flag for reverse sort. For more info, see +# http://docs.python.org/2/library/functions.html#sorted +# CREATE_INSTANCE_FLAVOR_SORT = { +# 'key': 'name', +# # or +# 'key': my_awesome_callback_method, +# 'reverse': False, +# } + +# The Horizon Policy Enforcement engine uses these values to load per service +# policy rule files. The content of these files should match the files the +# OpenStack services are using to determine role based access control in the +# target installation. + +# Path to directory containing policy.json files +POLICY_FILES_PATH = '/usr/share/openstack-dashboard/openstack_dashboard/conf' +# Map of local copy of service policy files +#POLICY_FILES = { +# 'identity': 'keystone_policy.json', +# 'compute': 'nova_policy.json', +# 'volume': 'cinder_policy.json', +# 'image': 'glance_policy.json', +# 'orchestration': 'heat_policy.json', +# 'network': 'neutron_policy.json', +#} + +# Trove user and database extension support. By default support for +# creating users and databases on database instances is turned on. +# To disable these extensions set the permission here to something +# unusable such as ["!"]. +# TROVE_ADD_USER_PERMS = [] +# TROVE_ADD_DATABASE_PERMS = [] + +LOGGING = { + 'version': 1, + # When set to True this will disable all logging except + # for loggers specified in this configuration dictionary. Note that + # if nothing is specified here and disable_existing_loggers is True, + # django.db.backends will still log unless it is disabled explicitly. + 'disable_existing_loggers': False, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'django.utils.log.NullHandler', + }, + 'console': { + # Set the level to "DEBUG" for verbose output logging. + 'level': 'INFO', + 'class': 'logging.StreamHandler', + }, + }, + 'loggers': { + # Logging from django.db.backends is VERY verbose, send to null + # by default. + 'django.db.backends': { + 'handlers': ['null'], + 'propagate': False, + }, + 'requests': { + 'handlers': ['null'], + 'propagate': False, + }, + 'horizon': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'openstack_dashboard': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'novaclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'cinderclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'keystoneclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'glanceclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'neutronclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'heatclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'ceilometerclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'troveclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'swiftclient': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'openstack_auth': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'nose.plugins.manager': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'django': { + 'handlers': ['console'], + 'level': 'INFO', + 'propagate': False, + }, + 'iso8601': { + 'handlers': ['null'], + 'propagate': False, + }, + 'scss': { + 'handlers': ['null'], + 'propagate': False, + }, + } +} + +# 'direction' should not be specified for all_tcp/udp/icmp. +# It is specified in the form. +SECURITY_GROUP_RULES = { + 'all_tcp': { + 'name': _('All TCP'), + 'ip_protocol': 'tcp', + 'from_port': '1', + 'to_port': '65535', + }, + 'all_udp': { + 'name': _('All UDP'), + 'ip_protocol': 'udp', + 'from_port': '1', + 'to_port': '65535', + }, + 'all_icmp': { + 'name': _('All ICMP'), + 'ip_protocol': 'icmp', + 'from_port': '-1', + 'to_port': '-1', + }, + 'ssh': { + 'name': 'SSH', + 'ip_protocol': 'tcp', + 'from_port': '22', + 'to_port': '22', + }, + 'smtp': { + 'name': 'SMTP', + 'ip_protocol': 'tcp', + 'from_port': '25', + 'to_port': '25', + }, + 'dns': { + 'name': 'DNS', + 'ip_protocol': 'tcp', + 'from_port': '53', + 'to_port': '53', + }, + 'http': { + 'name': 'HTTP', + 'ip_protocol': 'tcp', + 'from_port': '80', + 'to_port': '80', + }, + 'pop3': { + 'name': 'POP3', + 'ip_protocol': 'tcp', + 'from_port': '110', + 'to_port': '110', + }, + 'imap': { + 'name': 'IMAP', + 'ip_protocol': 'tcp', + 'from_port': '143', + 'to_port': '143', + }, + 'ldap': { + 'name': 'LDAP', + 'ip_protocol': 'tcp', + 'from_port': '389', + 'to_port': '389', + }, + 'https': { + 'name': 'HTTPS', + 'ip_protocol': 'tcp', + 'from_port': '443', + 'to_port': '443', + }, + 'smtps': { + 'name': 'SMTPS', + 'ip_protocol': 'tcp', + 'from_port': '465', + 'to_port': '465', + }, + 'imaps': { + 'name': 'IMAPS', + 'ip_protocol': 'tcp', + 'from_port': '993', + 'to_port': '993', + }, + 'pop3s': { + 'name': 'POP3S', + 'ip_protocol': 'tcp', + 'from_port': '995', + 'to_port': '995', + }, + 'ms_sql': { + 'name': 'MS SQL', + 'ip_protocol': 'tcp', + 'from_port': '1433', + 'to_port': '1433', + }, + 'mysql': { + 'name': 'MYSQL', + 'ip_protocol': 'tcp', + 'from_port': '3306', + 'to_port': '3306', + }, + 'rdp': { + 'name': 'RDP', + 'ip_protocol': 'tcp', + 'from_port': '3389', + 'to_port': '3389', + }, +} + +FLAVOR_EXTRA_KEYS = { + 'flavor_keys': [ + ('quota:disk_read_bytes_sec', _('Quota: Read bytes')), + ('quota:disk_write_bytes_sec', _('Quota: Write bytes')), + ('quota:cpu_quota', _('Quota: CPU')), + ('quota:cpu_period', _('Quota: CPU period')), + ('quota:vif_inbound_average', _('Quota: Inbound average')), + ('quota:vif_outbound_average', _('Quota: Outbound average')), + ('hw:cpu_sockets', _('Quota: CPU sockets')), + ('hw:cpu_cores', _('Quota: CPU cores')), + ('hw:cpu_threads', _('Quota: CPU threads')), + ('hw:cpu_max_sockets', _('Quota: Max CPU sockets')), + ('hw:cpu_max_cores', _('Quota: Max CPU cores')), + ('hw:cpu_max_threads', _('Quota: Max CPU threads')), + ] +} + +# Indicate to the Sahara data processing service whether or not +# automatic floating IP allocation is in effect. If it is not +# in effect, the user will be prompted to choose a floating IP +# pool for use in their cluster. False by default. You would want +# to set this to True if you were running Nova Networking with +# auto_assign_floating_ip = True. +# SAHARA_AUTO_IP_ALLOCATION_ENABLED = False + + +# A dictionary containing the settings for all databases to be used with +# Django. It is a nested dictionary whose contents maps database aliases +# to a dictionary containing the options for an individual database. +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'horizon', + 'USER': 'horizon', + 'PASSWORD': 'mypass', + 'HOST': '127.0.0.1', + 'PORT': '3306', + 'default-character-set': 'utf8' + }, +} + +# Boolean that decides if compression should also be done outside of the +# request/response loop - independent from user requests. This allows to +# pre-compress CSS and JavaScript files and works just like the automatic +# compression with the {% compress %} tag. +<% if @compress_offline %> +COMPRESS_OFFLINE = True +<% else %> +COMPRESS_OFFLINE = False +<% end %> + + +# The hash algorithm to use for authentication tokens. This must +# match the hash algorithm that the identity server and the +# auth_token middleware are using. Allowed values are the +# algorithms supported by Python's hashlib library. +OPENSTACK_TOKEN_HASH_ALGORITHM = 'md5' + +# Add additional plugins. + +# Allow for misc sections to be added + +# define a custom tmp upload directory (override /tmp) + +## MURANO_CONFIG_BEGIN ## +<% if @api_url != nil %>MURANO_API_URL = '<%= @api_url %>'<% end %> +<% if @repo_url != nil %>MURANO_REPO_URL = '<%= @repo_url %>'<% end %> +MAX_FILE_SIZE_MB = '<%= @max_file_size %>' +METADATA_CACHE_DIR = '<%= @metadata_dir %>' +LOGGING['loggers']['muranodashboard'] = {'handlers': ['console'], 'level': '<%= @dashboard_debug_level %>'} +LOGGING['loggers']['muranoclient'] = {'handlers': ['console'], 'level': '<%= @client_debug_level %>'} +<% if @enable_glare %> +MURANO_USE_GLARE = True +<% else %> +MURANO_USE_GLARE = False +<% end %> +## MURANO_CONFIG_END ## diff --git a/templates/default/openrc.erb b/templates/default/openrc.erb new file mode 100644 index 0000000..3fd9195 --- /dev/null +++ b/templates/default/openrc.erb @@ -0,0 +1,8 @@ +# COMMON MURANO ENVS + +<% if @hash[:enable_glare] %> +#export MURANO_PACKAGES_SERVICE='glare' +<% else %> +#export MURANO_PACKAGES_SERVICE='glance' +<% end %> +<% if @hash[:openrc_repo_url] != nil %>export MURANO_REPO_URL='<%= @hash[:openrc_repo_url] %>'<% end %>