Skip to content

Commit

Permalink
Merge pull request #16 from theko2fi/dev/0.3.0
Browse files Browse the repository at this point in the history
Dev/0.3.0
  • Loading branch information
theko2fi authored Mar 1, 2024
2 parents 5720747 + 140be9a commit 436b537
Show file tree
Hide file tree
Showing 27 changed files with 1,200 additions and 173 deletions.
1 change: 1 addition & 0 deletions .github/workflows/docs-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
init-html-short-title: Theko2fi.Multipass Collection Docs
init-extra-html-theme-options: |
documentation_home_url=https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/branch/main/
init-append-conf-py: "html_js_files=[('https://eu.umami.is/script.js', {'data-website-id': '0086c656-f41e-4131-ac3f-59bf72b1c4d8','defer': 'defer'})]"

publish-docs-gh-pages:
# for now we won't run this on forks
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ github_action_key*
.vscode/
dfdf.json
test-playbook.yml
changelogs/.plugin-cache.yaml
changelogs/.plugin-cache.yaml
plugins/modules/__pycache__/
ansible.cfg
25 changes: 25 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ Theko2Fi.Multipass Release Notes
.. contents:: Topics


v0.3.0
======

Release Summary
---------------

Release Date: 2024-02-29

The collection now contains module and option to manage directory mapping between host and Multipass virtual machines.


It also contains a Multipass driver for Molecule which allow to use Multipass instances for provisioning test resources.


Minor Changes
-------------

- molecule_multipass - a Multipass driver for Molecule.
- multipass_vm - add ``mount`` option which allows to mount host directories inside multipass instances.

New Modules
-----------

- theko2fi.multipass.multipass_mount - Module to manage directory mapping between host and Multipass virtual machine

v0.2.3
======

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Please note that this collection is **not** developed by [Canonical](https://can
- [multipass_vm_purge](https://theko2fi.github.io/ansible-multipass-collection/branch/main/multipass_vm_purge_module.html) - Module to purge all deleted Multipass virtual machines permanently
- [multipass_vm_transfer_into](https://theko2fi.github.io/ansible-multipass-collection/branch/main/multipass_vm_transfer_into_module.html) - Module to copy a file into a Multipass virtual machine
- [multipass_config_get](https://theko2fi.github.io/ansible-multipass-collection/branch/main/multipass_config_get_module.html) - Module to get Multipass configuration setting
- [multipass_mount](https://theko2fi.github.io/ansible-multipass-collection/branch/main/multipass_mount_module.html) - Module to manage directory mapping between host and Multipass virtual machines
* Roles:
- molecule_multipass - Molecule Multipass driver

## Installation

Expand Down
26 changes: 26 additions & 0 deletions changelogs/changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,29 @@ releases:
fragments:
- v0.2.3.yaml
release_date: '2024-01-05'
0.3.0:
changes:
minor_changes:
- molecule_multipass - a Multipass driver for Molecule.
- multipass_vm - add ``mount`` option which allows to mount host directories
inside multipass instances.
release_summary: 'Release Date: 2024-02-29
The collection now contains module and option to manage directory mapping
between host and Multipass virtual machines.
It also contains a Multipass driver for Molecule which allow to use Multipass
instances for provisioning test resources.
'
fragments:
- v0.3.0.yaml
modules:
- description: Module to manage directory mapping between host and Multipass virtual
machine
name: multipass_mount
namespace: ''
release_date: '2024-03-01'
10 changes: 10 additions & 0 deletions changelogs/fragments/v0.3.0.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
release_summary: |
Release Date: 2024-02-29
The collection now contains module and option to manage directory mapping between host and Multipass virtual machines.
It also contains a Multipass driver for Molecule which allow to use Multipass instances for provisioning test resources.
minor_changes:
- multipass_vm - add ``mount`` option which allows to mount host directories inside multipass instances.
- molecule_multipass - a Multipass driver for Molecule.
2 changes: 1 addition & 1 deletion galaxy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace: theko2fi
name: multipass

# The version of the collection. Must be compatible with semantic versioning
version: 0.2.3
version: 0.3.0

# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
readme: README.md
Expand Down
10 changes: 8 additions & 2 deletions plugins/module_utils/errors.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
#!/usr/bin/python
#
# Copyright (c) 2022, Kenneth KOFFI <https://www.linkedin.com/in/kenneth-koffi-6b1218178/>
# Copyright (c) 2024, Kenneth KOFFI (https://www.linkedin.com/in/kenneth-koffi-6b1218178/)
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

class MountExistsError(Exception):
pass

class MountNonExistentError(Exception):
pass

class MultipassFileTransferError(Exception):
pass

class MultipassContentTransferError(Exception):
pass

class SocketError(Exception):
pass
pass
64 changes: 51 additions & 13 deletions plugins/module_utils/multipass.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
import json
import time
from shlex import split as shlexsplit
from .errors import SocketError
from .errors import SocketError, MountNonExistentError, MountExistsError

def get_existing_mounts(vm_name):
vm = MultipassClient().get_vm(vm_name)
return vm.info().get('info').get(vm_name).get("mounts")

# Added decorator to automatically retry on unpredictable module failures
def retry_on_failure(ExceptionsToCheck, max_retries=5, delay=5, backoff=2):
Expand All @@ -29,6 +32,7 @@ class MultipassVM:
def __init__(self, vm_name, multipass_cmd):
self.cmd = multipass_cmd
self.vm_name = vm_name

# Will retry to execute info() if SocketError occurs
@retry_on_failure(ExceptionsToCheck=SocketError)
def info(self):
Expand All @@ -48,6 +52,7 @@ def info(self):
else:
raise Exception("Multipass info command failed: {0}".format(stderr.decode(encoding="utf-8")))
return json.loads(stdout)

def delete(self, purge=False):
cmd = [self.cmd, "delete", self.vm_name]
if purge:
Expand All @@ -67,8 +72,10 @@ def delete(self, purge=False):
self.vm_name, stderr.decode(encoding="utf-8")
)
)

def shell(self):
raise Exception("The shell command is not supported in the Multipass SDK. Consider using exec.")

def exec(self, cmd_to_execute, working_directory=""):
cmd = [self.cmd, "exec", self.vm_name]
if working_directory:
Expand All @@ -84,18 +91,21 @@ def exec(self, cmd_to_execute, working_directory=""):
if(exitcode != 0):
raise Exception("Multipass exec command failed: {0}".format(stderr.decode(encoding="utf-8")))
return stdout, stderr

def stop(self):
cmd = [self.cmd, "stop", self.vm_name]
try:
subprocess.check_output(cmd)
except:
raise Exception("Error stopping Multipass VM {0}".format(self.vm_name))

def start(self):
cmd = [self.cmd, "start", self.vm_name]
try:
subprocess.check_output(cmd)
except:
raise Exception("Error starting Multipass VM {0}".format(self.vm_name))

def restart(self):
cmd = [self.cmd, "restart", self.vm_name]
try:
Expand All @@ -109,6 +119,7 @@ class MultipassClient:
"""
def __init__(self, multipass_cmd="multipass"):
self.cmd = multipass_cmd

def launch(self, vm_name=None, cpu=1, disk="5G", mem="1G", image=None, cloud_init=None):
if(not vm_name):
# similar to Multipass's VM name generator
Expand All @@ -124,20 +135,24 @@ def launch(self, vm_name=None, cpu=1, disk="5G", mem="1G", image=None, cloud_ini
except:
raise Exception("Error launching Multipass VM {0}".format(vm_name))
return MultipassVM(vm_name, self.cmd)

def transfer(self, src, dest):
cmd = [self.cmd, "transfer", src, dest]
try:
subprocess.check_output(cmd)
except:
raise Exception("Multipass transfer command failed.")

def get_vm(self, vm_name):
return MultipassVM(vm_name, self.cmd)

def purge(self):
cmd = [self.cmd, "purge"]
try:
subprocess.check_output(cmd)
except:
raise Exception("Purge command failed.")

def list(self):
cmd = [self.cmd, "list", "--format", "json"]
out = subprocess.Popen(cmd,
Expand All @@ -148,6 +163,7 @@ def list(self):
if(not exitcode == 0):
raise Exception("Multipass list command failed: {0}".format(stderr))
return json.loads(stdout)

def find(self):
cmd = [self.cmd, "find", "--format", "json"]
out = subprocess.Popen(cmd,
Expand All @@ -158,30 +174,52 @@ def find(self):
if(not exitcode == 0):
raise Exception("Multipass find command failed: {0}".format(stderr))
return json.loads(stdout)
def mount(self, src, target):
cmd = [self.cmd, "mount", src, target]
try:
subprocess.check_output(cmd)
except:
raise Exception("Multipass mount command failed.")
def unmount(self, mount):
cmd = [self.cmd, "unmount", mount]
try:
subprocess.check_output(cmd)
except:
raise Exception("Multipass unmount command failed.")

def mount(self, src, target, mount_type='classic', uid_maps=[], gid_maps=[]):
mount_options = ["--type", mount_type]
for uid_map in uid_maps:
mount_options.extend(["--uid-map", uid_map])
for gid_map in gid_maps:
mount_options.extend(["--gid-map", gid_map])
cmd = [self.cmd, "mount"] + mount_options + [src, target]
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
_,stderr = out.communicate()
exitcode = out.wait()
stderr_cleaned = stderr.decode(encoding="utf-8").splitlines()
if(not exitcode == 0):
for error_msg in stderr_cleaned:
if 'is already mounted' in error_msg:
raise MountExistsError
raise Exception("Multipass mount command failed: {0}".format(stderr.decode(encoding="utf-8").rstrip()))

def umount(self, mount):
cmd = [self.cmd, "umount", mount]
out = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
_,stderr = out.communicate()
exitcode = out.wait()
stderr_cleaned = stderr.decode(encoding="utf-8").splitlines()
if(not exitcode == 0):
for error_msg in stderr_cleaned:
if 'is not mounted' in error_msg:
raise MountNonExistentError
raise Exception("{}".format(stderr.decode(encoding="utf-8").rstrip()))

def recover(self, vm_name):
cmd = [self.cmd, "recover", vm_name]
try:
subprocess.check_output(cmd)
except:
raise Exception("Multipass recover command failed.")

def suspend(self):
cmd = [self.cmd, "suspend"]
try:
subprocess.check_output(cmd)
except:
raise Exception("Multipass suspend command failed.")

def get(self, key):
cmd = [self.cmd, "get", key]
out = subprocess.Popen(cmd,
Expand Down
Loading

0 comments on commit 436b537

Please sign in to comment.