mirror of
https://github.com/inspec/inspec
synced 2024-11-23 05:03:07 +00:00
Adding inspec init profile for GCP. (#3484)
* Adding inspec init profile for GCP. * Adding final newline, thanks rubocop. * Ensure README headings are at the same level. * Move OS-specific default profile to new location * Enforce os-platform restriction on default profile template * Use profile templates in subdirs. * Updates to address PR feedback after rebasing from #3491. * Alter test setup to properly use YAML Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
329da7f679
commit
f64da78edb
13 changed files with 195 additions and 24 deletions
|
@ -6,23 +6,37 @@ require_relative 'renderer'
|
|||
module InspecPlugins
|
||||
module Init
|
||||
class CLI < Inspec.plugin(2, :cli_command)
|
||||
subcommand_desc 'init SUBCOMMAND', 'Initialize InSpec objects'
|
||||
subcommand_desc 'init SUBCOMMAND', 'Generate InSpec code'
|
||||
|
||||
# Look in the 'template' directory, and register a subcommand
|
||||
# for each template directory found there.
|
||||
template_dir = File.join(File.dirname(__FILE__), 'templates')
|
||||
Dir.glob(File.join(template_dir, '*')) do |template|
|
||||
template_name = Pathname.new(template).relative_path_from(Pathname.new(template_dir)).to_s
|
||||
#-------------------------------------------------------------------#
|
||||
# inspec init profile
|
||||
#-------------------------------------------------------------------#
|
||||
def self.valid_profile_platforms
|
||||
# Look in the 'template/profiles' directory and detect which platforms are available.
|
||||
profile_templates_dir = File.join(File.dirname(__FILE__), 'templates', 'profiles')
|
||||
Dir.glob(File.join(profile_templates_dir, '*')).select { |p| File.directory?(p) }.map { |d| File.basename(d) }
|
||||
end
|
||||
|
||||
# register command for the template
|
||||
desc "#{template_name} NAME", "Create a new #{template_name}"
|
||||
option :overwrite, type: :boolean, default: false,
|
||||
desc: 'Overwrites existing directory'
|
||||
define_method template_name.to_sym do |name_for_new_structure|
|
||||
renderer = InspecPlugins::Init::Renderer.new(self, options)
|
||||
renderer.render_with_values(template_name, name: name_for_new_structure)
|
||||
no_commands do
|
||||
def valid_profile_platforms
|
||||
self.class.valid_profile_platforms
|
||||
end
|
||||
end
|
||||
|
||||
desc 'profile [OPTIONS] NAME', 'Generate a new profile'
|
||||
option :platform, default: 'os', type: :string, aliases: [:p],
|
||||
desc: "Which platform to generate a platform for: choose from #{valid_profile_platforms.join(', ')}"
|
||||
option :overwrite, type: :boolean, default: false,
|
||||
desc: 'Overwrites existing directory'
|
||||
def profile(new_profile_name)
|
||||
unless valid_profile_platforms.include?(options[:platform])
|
||||
puts "Unable to generate profile: No template available for platform '#{options[:platform]}' (expected one of: #{valid_profile_platforms.join(', ')})"
|
||||
exit 1
|
||||
end
|
||||
template_path = File.join('profiles', options[:platform])
|
||||
renderer = InspecPlugins::Init::Renderer.new(self, options)
|
||||
renderer.render_with_values(template_path, name: new_profile_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,9 +16,9 @@ module InspecPlugins
|
|||
end
|
||||
|
||||
# rubocop: disable Metrics/AbcSize
|
||||
def render_with_values(template_type, template_values = {})
|
||||
def render_with_values(template_subdir_path, template_values = {})
|
||||
# look for template directory
|
||||
base_dir = File.join(File.dirname(__FILE__), 'templates', template_type)
|
||||
base_dir = File.join(File.dirname(__FILE__), 'templates', template_subdir_path)
|
||||
# prepare glob for all subdirectories and files
|
||||
template_glob = File.join(base_dir, '**', '{*,.*}')
|
||||
# Use the name attribute to define the path to the profile.
|
||||
|
@ -28,7 +28,10 @@ module InspecPlugins
|
|||
template_values[:name] = template_values[:name].split(%r{\\|\/}).last
|
||||
# Generate the full full_destination_root_path path on disk
|
||||
full_destination_root_path = Pathname.new(Dir.pwd).join(profile_path)
|
||||
ui.plain_text "Create new #{template_type} at #{ui.mark_text(full_destination_root_path)}"
|
||||
|
||||
# This is a bit gross
|
||||
generator_type = template_subdir_path.split(%r{[\/]}).first.sub(/s$/, '')
|
||||
ui.plain_text "Create new #{generator_type} at #{ui.mark_text(full_destination_root_path)}"
|
||||
|
||||
# check that the directory does not exist
|
||||
if File.exist?(full_destination_root_path) && !overwrite_mode
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
# Example InSpec Profile For GCP
|
||||
|
||||
This example shows the implementation of an InSpec profile for GCP that depends on the [InSpec GCP Resource Pack](https://github.com/inspec/inspec-gcp). See the [README](https://github.com/inspec/inspec-gcp) for instructions on setting up appropriate GCP credentials.
|
||||
|
||||
## Create a profile
|
||||
|
||||
```
|
||||
$ inspec init profile --platform gcp my-profile
|
||||
Create new profile at /Users/spaterson/my-profile
|
||||
* Create directory libraries
|
||||
* Create file README.md
|
||||
* Create directory controls
|
||||
* Create file controls/example.rb
|
||||
* Create file inspec.yml
|
||||
* Create file attributes.yml
|
||||
* Create file libraries/.gitkeep
|
||||
|
||||
```
|
||||
|
||||
## Update `attributes.yml` to point to your project
|
||||
|
||||
```
|
||||
gcp_project_id: 'my-gcp-project'
|
||||
```
|
||||
|
||||
## Run the tests
|
||||
|
||||
```
|
||||
$ cd gcp-profile/
|
||||
$ inspec exec . -t gcp:// --attrs attributes.yml
|
||||
|
||||
Profile: GCP InSpec Profile (my-profile)
|
||||
Version: 0.1.0
|
||||
Target: gcp://local-service-account@my-gcp-project.iam.gserviceaccount.com
|
||||
|
||||
✔ gcp-single-region-1.0: Ensure single region has the correct properties.
|
||||
✔ Region europe-west2 zone_names should include "europe-west2-a"
|
||||
✔ gcp-regions-loop-1.0: Ensure regions have the correct properties in bulk.
|
||||
✔ Region asia-east1 should be up
|
||||
✔ Region asia-northeast1 should be up
|
||||
✔ Region asia-south1 should be up
|
||||
✔ Region asia-southeast1 should be up
|
||||
✔ Region australia-southeast1 should be up
|
||||
✔ Region europe-north1 should be up
|
||||
✔ Region europe-west1 should be up
|
||||
✔ Region europe-west2 should be up
|
||||
✔ Region europe-west3 should be up
|
||||
✔ Region europe-west4 should be up
|
||||
✔ Region northamerica-northeast1 should be up
|
||||
✔ Region southamerica-east1 should be up
|
||||
✔ Region us-central1 should be up
|
||||
✔ Region us-east1 should be up
|
||||
✔ Region us-east4 should be up
|
||||
✔ Region us-west1 should be up
|
||||
✔ Region us-west2 should be up
|
||||
|
||||
|
||||
Profile: Google Cloud Platform Resource Pack (inspec-gcp)
|
||||
Version: 0.5.0
|
||||
Target: gcp://local-service-account@my-gcp-project.iam.gserviceaccount.com
|
||||
|
||||
No tests executed.
|
||||
|
||||
Profile Summary: 2 successful controls, 0 control failures, 0 controls skipped
|
||||
Test Summary: 18 successful, 0 failures, 0 skipped
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
# Below is to be uncommented and set with your GCP project ID:
|
||||
# gcp_project_id: 'your-gcp-project'
|
|
@ -0,0 +1,28 @@
|
|||
# encoding: utf-8
|
||||
# copyright: 2018, The Authors
|
||||
|
||||
title 'Sample Section'
|
||||
|
||||
gcp_project_id = attribute('gcp_project_id')
|
||||
|
||||
# you add controls here
|
||||
control 'gcp-single-region-1.0' do # A unique ID for this control
|
||||
impact 1.0 # The criticality, if this control fails.
|
||||
title 'Ensure single region has the correct properties.' # A human-readable title
|
||||
desc 'An optional description...'
|
||||
describe google_compute_region(project: gcp_project_id, name: 'europe-west2') do # The actual test
|
||||
its('zone_names') { should include 'europe-west2-a' }
|
||||
end
|
||||
end
|
||||
|
||||
# plural resources can be leveraged to loop across many resources
|
||||
control 'gcp-regions-loop-1.0' do # A unique ID for this control
|
||||
impact 1.0 # The criticality, if this control fails.
|
||||
title 'Ensure regions have the correct properties in bulk.' # A human-readable title
|
||||
desc 'An optional description...'
|
||||
google_compute_regions(project: gcp_project_id).region_names.each do |region_name| # Loop across all regions by name
|
||||
describe google_compute_region(project: gcp_project_id, name: region_name) do # The test for a single region
|
||||
it { should be_up }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
name: <%= name %>
|
||||
title: GCP InSpec Profile
|
||||
maintainer: The Authors
|
||||
copyright: The Authors
|
||||
copyright_email: you@example.com
|
||||
license: Apache-2.0
|
||||
summary: An InSpec Compliance Profile For GCP
|
||||
version: 0.1.0
|
||||
inspec_version: '>= 2.3.5'
|
||||
attributes:
|
||||
- name: gcp_project_id
|
||||
required: true
|
||||
description: 'The GCP project identifier.'
|
||||
type: string
|
||||
depends:
|
||||
- name: inspec-gcp
|
||||
url: https://github.com/inspec/inspec-gcp/archive/master.tar.gz
|
||||
supports:
|
||||
- platform: gcp
|
|
@ -6,3 +6,5 @@ copyright_email: you@example.com
|
|||
license: Apache-2.0
|
||||
summary: An InSpec Compliance Profile
|
||||
version: 0.1.0
|
||||
supports:
|
||||
platform: os
|
|
@ -1,5 +1,6 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'yaml'
|
||||
require_relative '../../../shared/core_plugin_test_helper.rb'
|
||||
|
||||
class InitCli < MiniTest::Test
|
||||
|
@ -17,6 +18,28 @@ class InitCli < MiniTest::Test
|
|||
end
|
||||
end
|
||||
|
||||
def test_generating_inspec_profile_with_explicit_platform
|
||||
Dir.mktmpdir do |dir|
|
||||
profile = File.join(dir, 'test-profile')
|
||||
out = run_inspec_process("init profile --platform os test-profile", prefix: "cd #{dir} &&")
|
||||
assert_equal 0, out.exit_status
|
||||
assert_includes out.stdout, 'Create new profile at'
|
||||
assert_includes out.stdout, profile
|
||||
assert_includes Dir.entries(profile).join, 'inspec.yml'
|
||||
assert_includes Dir.entries(profile).join, 'README.md'
|
||||
end
|
||||
end
|
||||
|
||||
def test_generating_inspec_profile_with_bad_platform
|
||||
Dir.mktmpdir do |dir|
|
||||
profile = File.join(dir, 'test-profile')
|
||||
out = run_inspec_process("init profile --platform nonesuch test-profile", prefix: "cd #{dir} &&")
|
||||
assert_equal 1, out.exit_status
|
||||
assert_includes out.stdout, 'Unable to generate profile'
|
||||
assert_includes out.stdout, "No template available for platform 'nonesuch'"
|
||||
end
|
||||
end
|
||||
|
||||
def test_profile_with_slash_name
|
||||
Dir.mktmpdir do |dir|
|
||||
profile = dir + '/test/deeper/profile'
|
||||
|
@ -27,4 +50,16 @@ class InitCli < MiniTest::Test
|
|||
assert_equal 'profile', profile['name']
|
||||
end
|
||||
end
|
||||
|
||||
def test_generating_inspec_profile_gcp
|
||||
Dir.mktmpdir do |dir|
|
||||
profile = File.join(dir, 'test-gcp-profile')
|
||||
out = run_inspec_process("init profile --platform gcp test-gcp-profile", prefix: "cd #{dir} &&")
|
||||
assert_equal 0, out.exit_status
|
||||
assert_includes out.stdout, 'Create new profile at'
|
||||
assert_includes out.stdout, profile
|
||||
assert_includes Dir.entries(profile).join, 'inspec.yml'
|
||||
assert_includes Dir.entries(profile).join, 'README.md'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'functional/helper'
|
||||
require 'fileutils'
|
||||
require 'tmpdir'
|
||||
require 'yaml'
|
||||
|
||||
describe 'profiles with git-based dependencies' do
|
||||
include FunctionalHelper
|
||||
|
@ -22,14 +23,15 @@ describe 'profiles with git-based dependencies' do
|
|||
CMD.run_command("git tag antag")
|
||||
end
|
||||
|
||||
File.open(File.join(@profile_dir, "inspec.yml"), 'a') do |f|
|
||||
f.write <<EOF
|
||||
depends:
|
||||
- name: git-dep
|
||||
git: #{@git_dep_dir}
|
||||
tag: antag
|
||||
EOF
|
||||
end
|
||||
inspec_yml = YAML.load(File.read(File.join(@profile_dir, "inspec.yml")))
|
||||
inspec_yml["depends"] = [
|
||||
{
|
||||
'name' => 'git-dep',
|
||||
'git' => @git_dep_dir,
|
||||
'tag' => 'antag'
|
||||
}
|
||||
]
|
||||
File.write(File.join(@profile_dir, "inspec.yml"), YAML.dump(inspec_yml))
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
|
|
Loading…
Reference in a new issue