def test_get_schema_doc_raises_key_errors(self): """get_schema_doc raises KeyErrors on missing keys.""" for key in self.required_schema: invalid_schema = copy(self.required_schema) invalid_schema.pop(key) with self.assertRaises(KeyError) as context_mgr: get_schema_doc(invalid_schema) self.assertIn(key, str(context_mgr.exception))
def test_get_schema_doc_properly_parse_description(self): """get_schema_doc description properly formatted""" full_schema = copy(self.required_schema) full_schema.update({ 'properties': { 'p1': { 'type': 'string', 'description': dedent("""\ This item has the following options: - option1 - option2 - option3 The default value is option1""") } } }) self.assertIn( dedent(""" **Config schema**: **p1:** (string) This item has the following options: - option1 - option2 - option3 The default value is option1 """), get_schema_doc(full_schema))
def test_get_schema_doc_returns_restructured_text_with_examples(self): """get_schema_doc returns indented examples when present in schema.""" full_schema = copy(self.required_schema) full_schema.update({ 'examples': { 'ex1': [1, 2, 3] }, 'properties': { 'prop1': { 'type': 'array', 'description': 'prop-description', 'items': { 'type': 'int' } } } }) self.assertIn( dedent(""" **Config schema**: **prop1:** (array of int) prop-description **Examples**:: ex1"""), get_schema_doc(full_schema))
def test_get_schema_doc_returns_restructured_text(self): """get_schema_doc returns restructured text for a cloudinit schema.""" full_schema = copy(self.required_schema) full_schema.update({ 'properties': { 'prop1': { 'type': 'array', 'description': 'prop-description', 'items': { 'type': 'integer' } } } }) self.assertEqual( dedent(""" name ---- **Summary:** title description **Internal name:** ``id`` **Module frequency:** frequency **Supported distros:** debian, rhel **Config schema**: **prop1:** (array of integer) prop-description\n\n"""), get_schema_doc(full_schema))
def test_get_schema_doc_handles_string_examples(self): """get_schema_doc properly indented examples as a list of strings.""" full_schema = copy(self.required_schema) full_schema.update({ 'examples': ['ex1:\n [don\'t, expand, "this"]', 'ex2: true'], 'properties': { 'prop1': { 'type': 'array', 'description': 'prop-description', 'items': { 'type': 'integer' } } } }) self.assertIn( dedent(""" **Config schema**: **prop1:** (array of integer) prop-description **Examples**:: ex1: [don't, expand, "this"] # --- Example2 --- ex2: true """), get_schema_doc(full_schema))
def test_get_schema_doc_handles_multiple_types(self): """get_schema_doc delimits multiple property types with a '/'.""" full_schema = copy(self.required_schema) full_schema.update( {'properties': { 'prop1': {'type': ['string', 'integer'], 'description': 'prop-description'}}}) self.assertIn( '**prop1:** (string/integer) prop-description', get_schema_doc(full_schema))
def test_get_schema_doc_handles_enum_types(self): """get_schema_doc converts enum types to yaml and delimits with '/'.""" full_schema = copy(self.required_schema) full_schema.update( {'properties': { 'prop1': {'enum': [True, False, 'stuff'], 'description': 'prop-description'}}}) self.assertIn( '**prop1:** (true/false/stuff) prop-description', get_schema_doc(full_schema))
def test_get_schema_doc_handles_nested_oneof_property_types(self): """get_schema_doc describes array items oneOf declarations in type.""" full_schema = copy(self.required_schema) full_schema.update( {'properties': { 'prop1': {'type': 'array', 'items': { 'oneOf': [{'type': 'string'}, {'type': 'integer'}]}, 'description': 'prop-description'}}}) self.assertIn( '**prop1:** (array of (string)/(integer)) prop-description', get_schema_doc(full_schema))
def test_get_schema_doc_handles_string_examples(self): """get_schema_doc properly indented examples as a list of strings.""" full_schema = copy(self.required_schema) full_schema.update( {'examples': ['ex1:\n [don\'t, expand, "this"]', 'ex2: true'], 'properties': { 'prop1': {'type': 'array', 'description': 'prop-description', 'items': {'type': 'integer'}}}}) self.assertIn( dedent(""" **Config schema**: **prop1:** (array of integer) prop-description **Examples**:: ex1: [don't, expand, "this"] # --- Example2 --- ex2: true """), get_schema_doc(full_schema))
def test_get_schema_doc_returns_restructured_text(self): """get_schema_doc returns restructured text for a cloudinit schema.""" full_schema = copy(self.required_schema) full_schema.update( {'properties': { 'prop1': {'type': 'array', 'description': 'prop-description', 'items': {'type': 'integer'}}}}) self.assertEqual( dedent(""" name ---- **Summary:** title description **Internal name:** ``id`` **Module frequency:** frequency **Supported distros:** debian, rhel **Config schema**: **prop1:** (array of integer) prop-description\n\n"""), get_schema_doc(full_schema))
'type': 'string', 'description': ( 'The version of the driver to install (e.g.' ' "390", "410"). Defaults to the latest' ' version.'), }, }, }, }, }, }, } OLD_UBUNTU_DRIVERS_STDERR_NEEDLE = ( "ubuntu-drivers: error: argument <command>: invalid choice: 'install'") __doc__ = get_schema_doc(schema) # Supplement python help() # Use a debconf template to configure a global debconf variable # (linux/nvidia/latelink) setting this to "true" allows the # 'linux-restricted-modules' deb to accept the NVIDIA EULA and the package # will automatically link the drivers to the running kernel. # EOL_XENIAL: can then drop this script and use python3-debconf which is only # available in Bionic and later. Can't use python3-debconf currently as it # isn't in Xenial and doesn't yet support X_LOADTEMPLATEFILE debconf command. NVIDIA_DEBCONF_CONTENT = """\ Template: linux/nvidia/latelink Type: boolean Default: true
def generate_docstring_from_schema(app, what, name, obj, options, lines): """Override module docs from schema when present.""" if what == 'module' and hasattr(obj, "schema"): del lines[:] lines.extend(get_schema_doc(obj.schema).split('\n'))
'local_repo_base_url': { 'type': 'string', 'description': dedent("""\ The base URL of an Alpine repository containing unofficial packages """) } }, 'required': [], 'minProperties': 1, # Either preserve_repositories or alpine_repo 'additionalProperties': False, } } } __doc__ = get_schema_doc(schema) def handle(name, cfg, cloud, log, _args): """ Call to handle apk_repos sections in cloud-config file. @param name: The module name "apk-configure" from cloud.cfg @param cfg: A nested dict containing the entire cloud config contents. @param cloud: The CloudInit object in use. @param log: Pre-initialized Python logger object to use for logging. @param _args: Any module arguments from cloud.cfg """ # If there is no "apk_repos" section in the configuration # then do nothing.
disabled altogether by setting ``resize_rootfs`` to ``false``."""), 'distros': distros, 'examples': [ 'resize_rootfs: false # disable root filesystem resize operation'], 'frequency': PER_ALWAYS, 'type': 'object', 'properties': { 'resize_rootfs': { 'enum': [True, False, NOBLOCK], 'description': dedent("""\ Whether to resize the root partition. Default: 'true'""") } } } __doc__ = get_schema_doc(schema) # Supplement python help() def _resize_btrfs(mount_point, devpth): # If "/" is ro resize will fail. However it should be allowed since resize # makes everything bigger and subvolumes that are not ro will benefit. # Use a subvolume that is not ro to trick the resize operation to do the # "right" thing. The use of ".snapshot" is specific to "snapper" a generic # solution would be walk the subvolumes and find a rw mounted subvolume. if (not util.mount_is_read_write(mount_point) and os.path.isdir("%s/.snapshots" % mount_point)): return ('btrfs', 'filesystem', 'resize', 'max', '%s/.snapshots' % mount_point) else: return ('btrfs', 'filesystem', 'resize', 'max', mount_point)