Skip to content

Commit

Permalink
Merge 83734c2 into 3c57805
Browse files Browse the repository at this point in the history
  • Loading branch information
rambleraptor authored Oct 23, 2018
2 parents 3c57805 + 83734c2 commit 9b4a5db
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 224 deletions.
9 changes: 9 additions & 0 deletions products/compute/ansible.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ overrides: !ruby/object:Provider::ResourceOverrides
Disk: !ruby/object:Provider::Ansible::ResourceOverride
editable: false
properties:
sourceSnapshot: !ruby/object:Provider::Ansible::PropertyOverride
description: |
The source snapshot used to create this disk. You can provide this as
a partial or full URL to the resource.
labels: !ruby/object:Provider::Ansible::PropertyOverride
version_added: '2.7'
type: !ruby/object:Provider::Ansible::PropertyOverride
Expand Down Expand Up @@ -276,6 +280,11 @@ overrides: !ruby/object:Provider::ResourceOverrides
version_added: '2.8'
RegionDisk: !ruby/object:Provider::Ansible::ResourceOverride
version_added: '2.8'
properties:
sourceSnapshot: !ruby/object:Provider::Ansible::PropertyOverride
description: |
The source snapshot used to create this disk. You can provide this as
a partial or full URL to the resource.
Route: !ruby/object:Provider::Ansible::ResourceOverride
properties:
description: !ruby/object:Provider::Ansible::PropertyOverride
Expand Down
236 changes: 75 additions & 161 deletions provider/ansible/documentation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,189 +11,82 @@
# See the License for the specific language governing permissions and
# limitations under the License.

require 'api/object'
require 'compile/core'
require 'provider/config'
require 'provider/core'
require 'provider/ansible/manifest'

# Rubocop doesn't like this file because the hashes are complicated.
# Humans like this file because the hashes are explicit and easy to read.
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
module Provider
module Ansible
# Responsible for building out YAML documentation blocks.
# rubocop:disable Metrics/ModuleLength
module Documentation
# Takes a long string and divides each string into multiple paragraphs,
# where each paragraph is a properly indented multi-line bullet point.
#
# Example:
# - This is a paragraph
# that wraps under
# the bullet properly
# - This is the second
# paragraph.
def bullet_lines(line, spaces)
line.split(".\n").map { |paragraph| bullet_line(paragraph, spaces) }
end

# Takes in a string (representing a paragraph) and returns a multi-line
# string, where each line is less than max_length characters long and all
# subsequent lines are indented in by spaces characters
#
# Example:
# - This is a sentence
# that wraps under
# the bullet properly
#
# - |
# This is a sentence
# that wraps under
# the bullet properly
# because of the :
# character
# rubocop:disable Metrics/AbcSize
def bullet_line(paragraph, spaces, _multiline = true, add_period = true)
paragraph += '.' unless paragraph.end_with?('.') || !add_period
paragraph = format_url(paragraph)
paragraph = paragraph.tr("\n", ' ').strip

# Paragraph placed inside array to get bullet point.
yaml = [paragraph].to_yaml
# YAML documentation header is not necessary.
yaml = yaml.gsub("---\n", '') if yaml.include?("---\n")

# YAML dumper isn't very smart about line lengths.
# If any line is over 160 characters (with indents), build the YAML
# block using wrap_field.
# Using YAML.dump output ensures that all character escaping done
if yaml.split("\n").any? { |line| line.length > (160 - spaces) }
return wrap_field(
yaml.tr("\n", ' ').gsub(/\s+/, ' '),
spaces + 3
).each_with_index.map { |x, i| i.zero? ? x : indent(x, 2) }
end
yaml
end
# rubocop:enable Metrics/AbcSize

# Builds out a full YAML block for DOCUMENTATION
# This includes the YAML for the property as well as any nested props
def doc_property_yaml(prop, object, spaces)
block = minimal_doc_block(prop, object, spaces)
# Ansible linter does not support nesting options this deep.
if prop.is_a?(Api::Type::NestedObject)
block.concat(nested_doc(prop.properties, object, spaces))
elsif prop.is_a?(Api::Type::Array) &&
prop.item_type.is_a?(Api::Type::NestedObject)
block.concat(nested_doc(prop.item_type.properties, object, spaces))
else
block
end
end

# Builds out a full YAML block for RETURNS
# This includes the YAML for the property as well as any nested props
def return_property_yaml(prop, spaces)
block = minimal_return_block(prop, spaces)
if prop.is_a? Api::Type::NestedObject
block.concat(nested_return(prop.properties, spaces))
elsif prop.is_a?(Api::Type::Array) &&
prop.item_type.is_a?(Api::Type::NestedObject)
block.concat(nested_return(prop.item_type.properties, spaces))
def to_yaml(obj)
if obj.is_a?(::Hash)
obj.reject { |_, v| v.nil? }.to_yaml.sub("---\n", '')
else
block
obj.to_yaml.sub("---\n", '')
end
end

private

# Find URLs and surround with U()
def format_url(paragraph)
paragraph.gsub(%r{
https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]
[a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+
[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))
[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9]\.[^\s]{2,}
}x, 'U(\\0)')
end

# Returns formatted nested documentation for a set of properties.
def nested_return(properties, spaces)
block = [indent('contains:', 4)]
block.concat(
properties.map do |p|
indent(return_property_yaml(p, spaces + 4), 8)
end
)
end

def nested_doc(properties, object, spaces)
block = [indent('suboptions:', 4)]
block.concat(
properties.map do |p|
indent(doc_property_yaml(p, object, spaces + 4), 8)
end
)
end

# Builds out the minimal YAML block for DOCUMENTATION
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/AbcSize
def minimal_doc_block(prop, _object, spaces)
required = prop.required && !prop.default_value ? 'true' : 'false'
[
"#{prop.name.underscore}:",
indent(
[
'description:',
# + 8 to compensate for name + description.
indent(bullet_lines(prop.description, spaces + 8), 4),
(indent(bullet_lines(resourceref_description(prop), spaces + 8), 4) \
# Builds out the DOCUMENTATION for a property.
# This will eventually be converted to YAML
def documentation_for_property(prop)
required = prop.required && !prop.default_value ? true : false
{
prop.name.underscore => {
'description' => [
format_description(prop.description),
(resourceref_description(prop) \
if prop.is_a?(Api::Type::ResourceRef) && !prop.resource_ref.readonly)
].compact, 4
),
indent([
"required: #{required}",
("default: #{prop.default_value}" if prop.default_value),
('type: bool' if prop.is_a? Api::Type::Boolean),
("aliases: [#{prop.aliases.join(', ')}]" if prop.aliases),
("version_added: #{prop.version_added}" if prop.version_added),
(if prop.is_a? Api::Type::Enum
[
'choices:',
"[#{prop.values.map { |x| quote_string(x.to_s) }.join(', ')}]"
].join(' ')
end)
].compact, 4)
]
].flatten.compact,
'required' => required,
'default' => (prop.default_value.to_s if prop.default_value),
'type' => ('bool' if prop.is_a? Api::Type::Boolean),
'aliases' => (prop.aliases if prop.aliases),
'version_added' => (prop.version_added.to_f if prop.version_added),
'choices' => (prop.values.map(&:to_s) if prop.is_a? Api::Type::Enum),
'suboptions' => (
if prop.is_a?(Api::Type::NestedObject)
prop.properties.map { |p| documentation_for_property(p) }.reduce({}, :merge)
elsif prop.is_a?(Api::Type::Array) && prop.item_type.is_a?(Api::Type::NestedObject)
prop.item_type.properties
.map { |p| documentation_for_property(p) }
.reduce({}, :merge)
end
)
}.reject { |_, v| v.nil? }
}
end
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/PerceivedComplexity

# Builds out the minimal YAML block for RETURNS
def minimal_return_block(prop, spaces)
# Builds out the RETURNS for a property.
# This will eventually be converted to YAML
def returns_for_property(prop)
type = python_type(prop)
# Complex types only mentioned in reference to RETURNS YAML block
# Complex types are nested objects traditionally, but arrays of nested
# objects will be included to avoid linting errors.
type = 'complex' if prop.is_a?(Api::Type::NestedObject) \
|| (prop.is_a?(Api::Type::Array) \
&& prop.item_type.is_a?(Api::Type::NestedObject))
[
"#{prop.name}:",
indent(
[
'description:',
# + 8 to compensate for name + description.
indent(bullet_lines(prop.description, spaces + 8), 4)
], 4
),
indent([
'returned: success',
"type: #{type}"
], 4)
]
{
prop.name => {
'description' => format_description(prop.description),
'returned' => 'success',
'type' => type,
'contains' => (
if prop.is_a?(Api::Type::NestedObject)
prop.properties.map { |p| returns_for_property(p) }.reduce({}, :merge)
elsif prop.is_a?(Api::Type::Array) && prop.item_type.is_a?(Api::Type::NestedObject)
prop.item_type.properties.map { |p| returns_for_property(p) }.reduce({}, :merge)
end
)
}.reject { |_, v| v.nil? }
}
end

def autogen_notice_contrib
Expand All @@ -212,7 +105,28 @@ def resourceref_description(prop)
"where the value is the #{prop.imports} of your #{prop.resource_ref.name}"
].join(' ')
end

# MM puts descriptions in a text block. Ansible needs it in bullets
def format_description(desc)
desc.split(".\n").map do |paragraph|
paragraph += '.' unless paragraph.end_with?('.')
paragraph = format_url(paragraph)
paragraph.tr("\n", ' ').strip.squeeze(' ')
end
end

# Find URLs and surround with U()
def format_url(paragraph)
paragraph.gsub(%r{
https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]
[a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+
[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))
[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9]\.[^\s]{2,}
}x, 'U(\\0)')
end
end
# rubocop:enable Metrics/ModuleLength
end
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/CyclomaticComplexity
# rubocop:enable Metrics/PerceivedComplexity
55 changes: 25 additions & 30 deletions templates/ansible/facts.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,23 @@ ANSIBLE_METADATA = {'metadata_version': <%= metadata_version -%>,

DOCUMENTATION = '''
---
module: <%= module_name(object) %>_facts
description:
- Gather facts for GCP <%= object.name %>
short_description: Gather facts for GCP <%= object.name %>
version_added: <%= lines(@config.manifest.get('version_added', object)) -%>
author: <%= lines(@config.manifest.get('author', object)) -%>
requirements:
<% @config.manifest.get('requirements', object).each do |line| -%>
<%= lines(indent(bullet_line(line, 4, false, false), 4)) -%>
<% end -%>
<% if object.facts.has_filters || !uri_props.empty? -%>
options:
<% end -%>
<% if object.facts.has_filters -%>
<%= object.facts.filter.name.underscore -%>:
description:
<%= lines(indent(wrap_field(object.facts.filter.description, 11), 11)) -%>
<% end -%>
<% uri_props.each do |prop| -%>
<%= lines(indent(doc_property_yaml(prop, object, 4), 4)) -%>
<% end -%>
extends_documentation_fragment: gcp
<%= to_yaml({
'module' => "#{module_name(object)}_facts",
'description' => ["Gather facts for GCP #{object.name}"],
'short_description' => "Gather facts for GCP #{object.name}",
'version_added' => @config.manifest.get('version_added', object).to_f,
'author' => @config.manifest.get('author', object),
'requirements' => @config.manifest.get('requirements', object),
'options' => [
({
object.facts.filter.name.underscore => {
'description' => format_description(object.facts.filter.description)
}
} if object.facts.has_filters),
uri_props.map { |p| documentation_for_property(p) }
].flatten.compact.reduce({}, :merge),
'extends_documentation_fragment' => 'gcp'
})-%>
'''

<% if example and example.facts -%>
Expand All @@ -56,14 +51,14 @@ EXAMPLES = '''
<% end -%>

RETURN = '''
items:
description: List of items
returned: always
type: complex
contains:
<% object.all_user_properties.each do |prop| -%>
<%= lines(indent(return_property_yaml(prop, 8), 8)) -%>
<% end -%>
<%= to_yaml({
'items' => {
'description' => 'List of items',
'returned' => 'always',
'type' => 'complex',
'contains' => object.all_user_properties.map { |p| returns_for_property(p) }.reduce({}, :merge)
}
})-%>
'''

################################################################################
Expand Down
Loading

0 comments on commit 9b4a5db

Please sign in to comment.