YaK is a platform-agnostic framework designed to streamline and automate the deployment of infrastructure and middleware components (database, K8S,etc..) across various environments. With its open-source core, YaK enables developers, DevOps teams, and IT professionals to manage infrastructure provisioning and component deployment efficiently, without being tied to a specific vendor. Whether you’re working with AWS, Azure, Oracle Cloud Infrastructure (OCI), or on-premises servers, YaK provides a consistent and flexible solution for your deployment needs. To have more informations, visit https://yak4all.io/
In this guide, we’ll walk you through the process of contributing to the YaK project by adding support for a new infrastructure provider, using OpenShift Virtualization as an example. We’ll cover the steps to fork the necessary repositories, implement the required changes, and integrate your contributions into the main project.

Overview of the YaK Project Architecture
YaK is organized into multiple GitLab repositories, each responsible for a specific part of the system, here the ones we will participate:
yakenv
: Environment container image with all the packages and dependenciesyak_core
: Is using yakenv as based image, core logicyak_runner
: Is using yak_core as based image, backend logic for automation jobsyak_ui
: Web-based frontend
These components work together to provide a modular, extensible framework for managing your different infrastructures and attached components.
Step 1: Fork the Repositories
Before starting development, fork each of the required repositories into your own GitLab account:
- Go to each project page:
- Click the Fork button at the top right of the page.
- Choose your namespace (e.g., your GitLab username or organization).
- Repeat for all repositories.
Step 2: Set Up Your Local Environment
Clone the forked repositories to your local machine:
git clone https://gitlab.com/<your-namespace>/yakenv.git
git clone https://gitlab.com/<your-namespace>/yak_core.git
git clone https://gitlab.com/<your-namespace>/yak_backend/yak_runner.git
git clone https://gitlab.com/<your-namespace>/yak_frontend/yak_ui.git
Then install dependencies (Docker or Podman) and ensure each project builds correctly. Refer to the README.md
files for project-specific setup instructions.
Step 3: Implement the New Infrastructure Provider
To add OpenShift Virtualization as a new provider, you will need to make coordinated changes across the different components:
yakenv
Dockerfile
: Add to this file the commands to install Ansible collections and/or CLI packages that you will need to interract with your new provider.
Here’s the lines we added for Openshift Virtualization:
# Install Kubevirt collection (OpenShift Virtualization)
RUN ansible-galaxy collection install kubevirt.core==2.1.0 --collections-path /etc/ansible/collections --force
# Install virtctl CLI (Kubevirt)
RUN curl -lL https://github.com/kubevirt/kubevirt/releases/download/v1.5.1/virtctl-v1.5.1-linux-amd64 -o /usr/bin/virtctl && chmod +x /usr/bin/virtctl
requirements.txt
: The python pip packages to install.
Here’s the line we added for Openshift Virtualization:
kubernetes==22.6.0
yak_core
collections/ansible_collections/yak/core/roles
: This is the directory were you will find all the tasks to manage the infrastructures and the servers storage.
In this directory you will find theinfrastructures
which is a template for a new infrastructures, you can copy it and rename it as the name of your infrastructure (replace white spaces by underscores_
) hereopenshift_virtualization
collections/ansible_collections/yak/core/roles/openshift_virtualization/defaults/main.yml
: Define the variables defaults values:
---
state: present
openshift_namespace: default
custom_labels: {}
instance_type: u1.medium
kubeconfig_path: ~/yak/kubeconfig
preference_name: ""
rhel_activation_key: "{{ lookup('env','RHEL_ACTIVATION_KEY') }}"
rhel_org_id: "{{ lookup('env','RHEL_ORG_ID') }}"
collections/ansible_collections/yak/core/roles/openshift_virtualization/tasks
: the tasks files for your infrastructure. There is the minimum files:
main.yml
: the default task file, it will redirect to the tasks depending of the state. Here is the content:
---
- include_tasks: check_credential.yml
- include_tasks: "{{ state }}.yml"
check_credentials.yml
: Step to valid that the provided credentials are valid
---
- block:
- name: List all VMs - Verifying credentials are valid
delegate_to: localhost
kubevirt.core.kubevirt_vm_info:
namespace: "{{ openshift_namespace }}"
retries: 3
delay: 5
rescue:
- fail:
msg: 'Please ensure your OpenShift credentials are valid.'
present.yml
: Steps to create the server’s VM on your infrastructure. If there is some differences to create a linux or windows server, you can include sub-tasks files (here we developedcreate_linux.yml
andcreate_windows.yml
)
# Copyright: (c) 2025, dbi services, distributed without any warranty under the terms of the GNU General Public License v3
- ansible.builtin.include_tasks:
file: get_vm_state.yml
- delegate_to: localhost
set_fact:
number_of_vms: "{{ r_get_vm_state.resources | length }}"
- block:
- include_tasks: "create_{{ os_type }}.yml"
- include_tasks: post_config.yml
when: number_of_vms | int == 0
- when: number_of_vms | int > 0
block:
- fail:
msg: "Cannot Deploy as the VirtualMachine already exists."
started.yml
: Steps to start the server’s VM
---
- name: Power On
delegate_to: localhost
ansible.builtin.shell: "virtctl --kubeconfig {{ openshift_virtualization_kubeconfig }} --namespace {{ openshift_namespace }} start {{ machine_name }}"
register: r_openshift_virtualization_vm_started
- include_tasks: "post_config.yml"
stopped.yml
: Steps to power off your server
---
- name: Power Off
delegate_to: localhost
ansible.builtin.shell: "virtctl --kubeconfig {{ openshift_virtualization_kubeconfig }} --namespace {{ openshift_namespace }} stop {{ machine_name }}"
register: r_openshift_virtualization_vm_stopped
absent.yml
: Steps to destroy the server
---
- name: Delete the VirtualMachine
delegate_to: localhost
kubevirt.core.kubevirt_vm:
namespace: "{{ openshift_namespace }}"
state: absent
name: "{{ machine_name }}"
register: r_openshift_virtualization_vm_absent
yak_runner
sql/upgrade/2.2.0/02-infrastructures.sql
: Add in the infrastructures jsonb specification schema, the new provider with its specific parameters
UPDATE jsonb_specifications
SET schema =
'{
"variableName": "variables",
"dataType": "object",
"niceName": "Variables",
"isSelectable": true,
"selectorType": "providerName",
"selectorOption": [
"aws",
"azure",
"oci",
"openshift_virtualization"
],
"aws": [
...
],
"azure": [
...
],
"oci": [
...
],
"openshift_virtualization": [
{
"dataType": "string",
"defaultValue": "default",
"isOneOffSetting": false,
"maxLength": 100,
"mandatory": true,
"variableName": "network_name",
"usage": "Network name to connect",
"regex": null,
"niceName": "Network Name",
"minLength": 1
},
{
"dataType": "string",
"defaultValue": "default",
"isOneOffSetting": false,
"maxLength": 100,
"mandatory": true,
"variableName": "domain_device_interface_name",
"usage": "Domain device interface name",
"regex": null,
"niceName": "Domain Device Interface Name",
"minLength": 1
},
{ "variableName": "custom_labels", "niceName": "Infrastructure labels", "dataType": "dictionary", "mandatory": false }
]
}
'::jsonb
WHERE name = 'infrastructureVariables';
sql/upgrade/2.2.0/05-server_secret_type_parameters.sql
: Add in the according tables the new provider and if needed, new secret type with its parameters:
insert into providers values
(5, 'openshift_virtualization', true, '{}', 'OpenShift Virtualization');
insert into secret_types values
(7, 'kubeconfig',true,true,false,5);
-- Data: secret_type_parameters
insert into secret_type_parameters (secret_type_id, variable_name, nice_name, data_type_id, mandatory, options, min_length, max_length) values
-- Kubeconfig
(7, 'K8S_AUTH_KUBECONFIG', 'OpenShift Kubeconfig', 1, true, null, 1, 5120);
sql/upgrade/2.2.0/06-provider_images.sql
: Add new images for your new provider and allow admin to manage them:
insert into provider_images (name, provider_id, operating_system_type_id, ansible_user, variables, user_id) values
-- OpenShift Virtualization linux
('datasource_centos-stream9', 5, 1, 'admin', '{"dataSource": {"name": "centos-stream9", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_centos-stream10', 5, 1, 'admin', '{"dataSource": {"name": "centos-stream10", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_fedora', 5, 1, 'admin', '{"dataSource": {"name": "fedora", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_rhel8', 5, 1, 'admin', '{"dataSource": {"name": "rhel8", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_rhel9', 5, 1, 'admin', '{"dataSource": {"name": "rhel9", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_ubuntu24-04', 5, 1, 'admin', '{"dataSource": {"name": "ubuntu24-04", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_win2k22', 5, 1, 'admin', '{"dataSource": {"name": "win2k22", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_win2k25', 5, 1, 'admin', '{"dataSource": {"name": "win2k25", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_win10', 5, 1, 'admin', '{"dataSource": {"name": "win10", "namespace": "openshift-virtualization-os-images"}}', 1),
('datasource_win11', 5, 1, 'admin', '{"dataSource": {"name": "win11", "namespace": "openshift-virtualization-os-images"}}', 1);
insert into projects_provider_images (project_id, image_id, user_id)
select 1, id, 1 from provider_images where provider_id=5;
sql/upgrade/2.2.0/07-servers.sql
: Adding server parameters for the new provider :
UPDATE jsonb_specifications
SET schema =
'{
"variableName": "variables",
"niceName": "Variables",
"dataType": "object",
"isSelectable": true,
"selectorType": "providerName",
"selectorOption": ["aws", "azure", "oci", "openshift_virtualization", "on_premises"],
"aws": [
...
],
"azure": [
...
],
"oci": [
...
],
"on_premises": [
...
],
"openshift_virtualization": [
{
"variableName": "providerImageId",
"niceName": "Provider Image ID",
"dataType": "integer",
"hidden": true,
"mandatory": false,
"isOneOffSetting": false
},
{
"dataType": "string",
"defaultValue": null,
"isOneOffSetting": true,
"maxLength": 100,
"mandatory": true,
"variableName": "providerImageName",
"usage": "See documentation",
"regex": null,
"options": "values.vProviderImages",
"niceName": "Provider Image Name",
"minLength": 1
},
{
"variableName": "providerShapeId",
"niceName": "Provider Shape ID",
"dataType": "integer",
"hidden": true,
"mandatory": false,
"isOneOffSetting": false
},
{
"dataType": "string",
"defaultValue": null,
"isOneOffSetting": true,
"maxLength": 100,
"mandatory": true,
"variableName": "providerShapeName",
"usage": "See documentation",
"regex": null,
"options": "values.vProviderShapes",
"niceName": "Provider Shape Name",
"minLength": 1
},
{
"variableName": "openshift_namespace",
"niceName": "Namespace",
"defaultValue": "default",
"dataType": "string",
"mandatory": true
},
{
"variableName": "preference_name",
"niceName": "Preference Name",
"defaultValue": "",
"dataType": "string",
"mandatory": true
},
{ "variableName": "custom_labels", "niceName": "Server labels", "dataType": "dictionary", "mandatory": false }
]
}' :: jsonb
WHERE name = 'serverVariables';
sql/upgrade/2.2.0/11-components-storage-jsonb.sql
: Adding component disks parameters for the provider:
UPDATE jsonb_specifications
SET schema =
'{
"dataType": "object",
"on_premises": [],
"isSelectable": true,
"selectorType": "providerName",
"variableName": "storageVariables",
"selectorOption": [
"aws",
"azure",
"oci",
"on_premises",
"openshift_virtualization"
],
"aws": [
...
],
"azure": [
...
],
"oci": [
...
],
"on_premises": [
...
],
"openshift_virtualization": [
{
"dataType": "array",
"isSelectable": true,
"selectorType": "osType",
"variableName": "filesystems",
"niceName": "File systems",
"selectorOption": [
"Linux",
"Windows"
],
"Linux": [
{
"variableName": "mount_point",
"niceName": "Mount point, e.g. /app",
"dataType": "string",
"defaultValue": null,
"isOneOffSetting": true,
"maxLength": 100,
"mandatory": true,
"minLength": 1
},
{
"variableName": "filesystem_type",
"niceName": "Filesystem type",
"dataType": "string",
"mandatory": true,
"isOneOffSetting": false,
"defaultValue": "xfs"
},
{
"variableName": "size_gb",
"niceName": "Mount point size in GB",
"dataType": "integer",
"mandatory": true,
"isOneOffSetting": false,
"defaultValue": 20
},
{
"dataType": "integer",
"isOneOffSetting": true,
"mandatory": true,
"variableName": "max_size_gb",
"niceName": "Disk size",
"min": 1,
"defaultValue": -1,
"usage": "Defines the size of the virtual disk at cloud provider end. 5% overhead is automatically added to each disk for metadata storage."
},
{
"dataType": "string",
"niceName": "Storage Class Name",
"mandatory": false,
"maxLength": 100,
"minLength": 0,
"defaultValue": "",
"variableName": "volume_storageClassName",
"isOneOffSetting": true
}
],
"Windows": [
{
"variableName": "drive_letter",
"niceName": "Drive letter, e.g. F",
"dataType": "string",
"defaultValue": null,
"isOneOffSetting": true,
"maxLength": 100,
"mandatory": true,
"minLength": 1
},
{
"variableName": "partition_label",
"niceName": "Partition label",
"dataType": "string",
"mandatory": true,
"isOneOffSetting": false,
"defaultValue": "data"
},
{
"variableName": "size_gb",
"niceName": "Mount point size in GB",
"dataType": "integer",
"mandatory": true,
"isOneOffSetting": false,
"defaultValue": 20
},
{
"dataType": "string",
"niceName": "Storage Class Name",
"mandatory": false,
"maxLength": 100,
"minLength": 0,
"defaultValue": "",
"variableName": "volume_storageClassName",
"isOneOffSetting": true
}
]
}
]
}' :: jsonb
WHERE name = 'componentStorageVariables';
sql/upgrade/2.2.0/12-provider_disks_parameters_root_jsonb.sql
: Adding server disks parameters for the provider:
UPDATE jsonb_specifications
SET schema =
'{
"dataType": "object",
"on_premises": [],
"isSelectable": true,
"selectorType": "providerName",
"variableName": "storageVariables",
"selectorOption": [
"aws",
"azure",
"oci",
"on_premises",
"openshift_virtualization"
],
"aws": [
...
],
"azure": [
...
],
"oci": [
...
],
"on_premises": [
...
],
"openshift_virtualization": [
{
"Linux": [
{
"dataType": "integer",
"niceName": "Size in GB",
"mandatory": true,
"defaultValue": 30,
"variableName": "volume_size",
"isOneOffSetting": true
},
{
"dataType": "string",
"niceName": "Storage Class Name",
"mandatory": false,
"maxLength": 100,
"minLength": 0,
"defaultValue": "",
"variableName": "volume_storageClassName",
"isOneOffSetting": true
},
{
"options": [
"Block",
"Filesystem"
],
"dataType": "string",
"niceName": "Disk type",
"mandatory": true,
"maxLength": 100,
"minLength": 1,
"defaultValue": "Filesystem",
"variableName": "volume_mode",
"isOneOffSetting": true
}
],
"Windows": [
{
"dataType": "integer",
"niceName": "Size in GB",
"mandatory": true,
"defaultValue": 30,
"variableName": "volume_size",
"isOneOffSetting": true
},
{
"dataType": "string",
"niceName": "Storage Class Name",
"mandatory": false,
"maxLength": 100,
"minLength": 0,
"defaultValue": "",
"variableName": "volume_storageClassName",
"isOneOffSetting": true
},
{
"options": [
"Block",
"Filesystem"
],
"dataType": "string",
"niceName": "Disk type",
"mandatory": true,
"maxLength": 100,
"minLength": 1,
"defaultValue": "Filesystem",
"variableName": "volume_mode",
"isOneOffSetting": true
}
],
"dataType": "object",
"isSelectable": true,
"selectorType": "providerImageOsType",
"variableName": "openshift_virtualization_root_disk_parameters",
"selectorOption": [
"Linux",
"Windows"
]
},
{
"variableName": "providerImageOsType",
"niceName": "Operating System",
"dataType": "string",
"mandatory": true,
"isOneOffSetting": true,
"options": ["Linux", "Windows"],
"readonly": true
}
]
}' :: jsonb
WHERE name = 'componentStorageVariables';
sql/upgrade/2.2.0/13-provider-shapes.sql
: Adding shapes for the provider:
insert into provider_shapes (name, provider_id, variables, user_id) values
('openshift-virtualization-u1_medium-shape', 5, '{"instance_type": "u1.medium"}', 1),
('openshift-virtualization-u1_large-shape', 5, '{"instance_type": "u1.large"}', 1),
('openshift-virtualization-u1_xlarge-shape', 5, '{"instance_type": "u1.xlarge"}', 1);
insert into projects_provider_shapes (project_id, shape_id, user_id)
select 1, id, 1 from provider_shapes where provider_id=5;
yak_ui
yak-ui/src/assets/providers/virt-icon.png
: Adding the new provider logo in provider logos:yak-ui/src/utils/imageList.ts
: Adding the new logo path in theProviderLogos
dict:
import OnPremiseLogo from "@/assets/providers/2860404-200-min.png";
import LinuxLogo from "@/assets/linux.png";
import WindowsLogo from "@/assets/windows.png";
import OpenShiftVirtualizationLogo from "@/assets/providers/virt-icon.png";
export const ProviderLogos: HashTable<any> = {
aws: AwsLogo,
oci: OciLogo,
'on_premises': OnPremiseLogo,
azure: AzureLogo,
'openshift_virtualization': OpenShiftVirtualizationLogo,
};
...
yak-ui/src/components/YakTabContainer.vue
: Adding incontainerClass.specialTabs
andcardClass.specialTabs
lists the new provider :
...
const containerClass = computed(() => {
const specialTabs = ["aws", "oci", "azure", "on_premises", "openshift_virtualization"];
return specialTabs.includes(tab.value)
? "container-grid-custom"
: "container-custom";
});
const cardClass = computed(() => {
const specialTabs = ["aws", "oci", "azure", "on_premises", "openshift_virtualization"];
return specialTabs.includes(tab.value) ? "card-grid-custom" : "card-custom";
});
...
yak-ui/src/components/customs/StorageEditor.vue
: Adding in
list the new provider :providerTypeField
.options
...
const providerTypeField: Field = {
variableName: "providerName",
niceName: "providerName",
dataType: "string",
mandatory: true,
isOneOffSetting: true,
options: ["aws", "azure", "oci", "on_premises", "openshift_virtualization"],
};
...
yak-ui/src/data_structures/vComponents.ts
: Adding inchildren.options
list parameter for
call the new provider :allGroups.push
...
allGroups.push({
variableName: group.name,
niceName: "group " + group.name,
mandatory: true,
dataType: "object",
isOneOffSetting: false,
children: [{
variableName: "providerName",
niceName: "Provider",
dataType: "string",
mandatory: true,
isOneOffSetting: true,
options: ["aws", "azure", "oci", "on_premises", "openshift_virtualization"],
...
yak-ui/src/data_structures/vSecrets.ts
: Adding in
list the new secret type :vSecretTypeName
...
vSecretTypeName: [
"aws credentials",
"azure credentials",
"oci credentials",
"kubeconfig",
"ssh key",
"winrm",
],
...
Step 4: Test Your Changes
Each component has its own set of unit and integration tests. Be sure to:
- Run tests locally with each change.
All sub-projects of the YaK have a Dockerfile on root directory. Build them in this order to respect the dependencies:- yakenv
- yak_core
- yak_runner
- yak_postgres
- yak_graphile
- yak_ui
Once images are built, run them on Docker/Podman on your computer, or on a Container orchestrator (Kubernetes/Openshift/…)
- Validate that your provider appears correctly in the UI and can be selected.
New infrastructure declaration:

New infrastructure declared:

New secret declaration:

Provider images appeared well:

Provider shapes appeared well:

New server declaration:



- Test full cluster lifecycle operations (deploy, stop, start, destroy, delete) for OpenShift Virtualization.
Deploy server:




Result in Openshift:

Stop server:


Start server:


Destroy server:


Step 5: Contribute Your Changes
Once development is complete and tested:
- Push your changes to your fork.
- Create a Merge Request (MR) from your fork to the main project repository for each affected sub-project:
- In your MR description, include:
- A summary of the changes
- A test plan and screenshots if applicable
- Any known limitations or issues

Follow the contribution guidelines of the YaK project for naming conventions, commit messages, and code standards.
You can take my merge requests as an exemple:
- yakenv: https://gitlab.com/yak4all/yakenv/-/merge_requests/12
- yak_core: https://gitlab.com/yak4all/yak_core/-/merge_requests/20
- yak_runner: http://gitlab.com/yak4all/yak_backend/yak_runner/-/merge_requests/42
- yak_ui: https://gitlab.com/yak4all/yak_frontend/yak_ui/-/merge_requests/42
Step 6: Collaborate with the Community
After submitting your MR:
- Respond to any feedback or questions from project maintainers.
- Be open to revising your code based on review suggestions.
- Once approved, your changes will be merged into the main project!
Conclusion
Adding a new infrastructure provider to YaK, such as OpenShift Virtualization, is a powerful way to contribute to the project and support broader use cases. With a clear architecture and modular codebase, YaK makes it straightforward for developers to extend its capabilities.
YaK can also be integrated with various Red Hat products to enhance your automation workflows – check out our guide on Integrating YaK into Red Hat Ansible Automation Platform to learn more.