diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..0daf12d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,8 @@
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000..eec5b84
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,5 @@
+# These owners will be the default owners for everything in
+# the repo, unless a later match takes precedence:
+#
+# @bdsoha will be requested for review when someone opens a pull request.
+* @bdsoha
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
new file mode 100644
index 0000000..1366a06
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -0,0 +1,55 @@
+---
+name: ๐ Bug Report
+description: Report an issue to help the project improve.
+title: ๐
+labels:
+ - "type: bug ๐"
+body:
+ - type: checkboxes
+ attributes:
+ label: Is there an existing issue for this?
+ description: Please search to see if an issue already exists for the bug you encountered.
+ options:
+ - label: I have searched the existing issues
+ required: true
+
+ - type: input
+ attributes:
+ label: Version
+ description: Docker image digest
+ placeholder: sha256:58025ebfabf03b54899829b7d20b1c9c3de3558f452edd033802e809a8c194b0
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Current Behavior
+ description: A concise description of what you're experiencing.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Expected Behavior
+ description: A concise description of what you expected to happen.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Steps To Reproduce & Additional information
+ description: |
+ Steps to reproduce the behavior:
+
+ - Links
+ - References
+ - More context about the issue you are encountering!
+
+ Tip: You can attach images by dragging files into this area.
+ placeholder: |
+ 1. In this environment...
+ 1. With this config...
+ 1. Run '...'
+ 1. See error...
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/docs_request.yaml b/.github/ISSUE_TEMPLATE/docs_request.yaml
new file mode 100644
index 0000000..cbf8f34
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/docs_request.yaml
@@ -0,0 +1,26 @@
+---
+name: ๐ Documentation Change
+description: Improvements or additions to documentation.
+title: ๐
+labels:
+ - "type: documentation ๐"
+body:
+ - type: checkboxes
+ attributes:
+ label: Is there an existing request for this change?
+ description: |
+ Please make sure a request does not already exists for the
+ documentation changes you're looking to implement.
+ options:
+ - label: I have searched the existing issues
+ required: true
+ - type: textarea
+ attributes:
+ label: Context for documentation change
+ description: Explain what led you to draft this issue.
+ placeholder: |
+ I noticed unclear explanation of build and install and encountered
+ a few issues while attempting it myself.
+ This could confuse other contributors.
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
new file mode 100644
index 0000000..8666182
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -0,0 +1,32 @@
+---
+name: ๐ Feature Request
+description: Request a new feature.
+title: ๐
+labels:
+ - "type: feature request ๐"
+body:
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ description: A clear and concise description of the feature you're interested in.
+ placeholder: Describe in a few lines your feature request.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Suggested Solution
+ description: |
+ Describe the solution you'd like.
+ A clear and concise description of what you want to happen.
+ validations:
+ required: true
+
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: |
+ Add any other context about the problem here.
+ validations:
+ required: false
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
new file mode 100644
index 0000000..49ca16e
--- /dev/null
+++ b/.github/workflows/build.yaml
@@ -0,0 +1,73 @@
+---
+name: ๐ทโโ๏ธ Build
+
+on:
+ push:
+ branches:
+ - main
+
+ tags:
+ - v*
+
+jobs:
+ lint:
+ name: ๐งน Lint
+ runs-on: ubuntu-latest
+ if: github.actor != 'renovate[bot]' && github.actor != 'renovate[bot]'
+ steps:
+ - name: ๐ Checkout repository
+ uses: actions/checkout@v4
+
+ - name: ๐งน Lint
+ uses: pre-commit/action@v3.0.1
+
+ build:
+ name: ๐ณ Docker Build
+ runs-on: ubuntu-latest
+ needs:
+ - lint
+
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - name: ๐ Checkout repository
+ uses: actions/checkout@v4
+
+ - uses: docker/setup-qemu-action@v3
+ - uses: docker/setup-buildx-action@v3
+
+ - name: ๐ Login to GHCR
+ uses: docker/login-action@v3
+ with:
+ registry: ghcr.io
+ username: ${{ github.repository_owner }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: ๐ Docker metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: ghcr.io/${{ github.repository }}
+ flavor: |
+ latest=false
+ tags: |
+ type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
+ type=ref,event=pr,prefix=pr-
+ type=semver,pattern=v{{version}}
+ type=semver,pattern=v{{major}}.{{minor}}
+
+ - name: ๐ณ Docker Build & Push
+ uses: docker/build-push-action@v6
+ id: docker_build
+ env:
+ DOCKER_BUILD_NO_SUMMARY: true
+ with:
+ cache-from: type=gha
+ cache-to: type=gha,mode=max
+ context: .
+ labels: ${{ steps.meta.outputs.labels }}
+ push: true
+ provenance: false
+ tags: ${{ steps.meta.outputs.tags }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..8753d72
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,30 @@
+---
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
+ hooks:
+ - id: check-added-large-files
+ args:
+ - --maxkb=300
+ - id: check-json
+ - id: check-toml
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: mixed-line-ending
+ - id: trailing-whitespace
+
+ - repo: https://github.com/adrienverge/yamllint
+ rev: v1.35.1
+ hooks:
+ - id: yamllint
+ args:
+ - --config-file
+ - src/home/.config/yamllint/config
+
+ - repo: https://github.com/igorshubovych/markdownlint-cli
+ rev: v0.39.0
+ hooks:
+ - id: markdownlint
+ args:
+ - --config
+ - src/home/.config/markdownlint/config
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c5548b0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,93 @@
+ARG tag=12
+
+################################## Temp Layer ##################################
+
+FROM debian:${tag} AS temp
+
+RUN mkdir -p /etc/apt/keyrings
+
+### Install helper requirements
+RUN apt-get update \
+ && apt-get satisfy -y --no-install-recommends \
+ "ca-certificates (>=20230311)" \
+ "curl (>=7.88)" \
+ "gnupg (>=2.2)" \
+ "unzip (>=6.0)" \
+ && rm -rf /var/lib/apt/lists/*
+
+### Add non-standard repository keys
+RUN --mount=src=src,dst=/build \
+ /build/add-gpg-keyrings.sh /build/extra.gpg.txt \
+ && chmod a+r /etc/apt/keyrings/*.gpg
+
+################################## Base Layer ##################################
+
+FROM debian:${tag} AS base
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+
+ENV LANG=en_US.UTF-8
+
+COPY src/rootfs /
+COPY --from=temp /etc/apt/keyrings /etc/apt/keyrings
+
+### Add application user
+RUN adduser \
+ --disabled-password \
+ --gecos '' \
+ --uid 1000 \
+ kloud
+
+### Install packages
+RUN --mount=src=src,dst=/build \
+ apt-get update \
+ && apt-get satisfy -y --no-install-recommends \
+ "ca-certificates (>=20230311)" \
+ && apt-get update \
+ && apt-get satisfy -y --no-install-recommends $(cat /build/packages.apt) \
+ && rm -rf \
+ /var/lib/apt/lists/* \
+ /usr/lib/python3.11/EXTERNALLY-MANAGED \
+ && locale-gen \
+ && ln -fs "$(which python3)" /usr/bin/python
+
+############################### Dependency Layer ###############################
+
+FROM temp AS deps
+WORKDIR /usr/local/bin
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+ARG TARGETARCH
+
+# renovate: source=github-releases dep=google/go-containerregistry
+ARG crane_version=0.20.0
+
+RUN case ${TARGETARCH} in "arm64") amr64 ;; "amd64") file=x86_64 ;; esac \
+ && curl -fsSL "https://github.com/google/go-containerregistry/releases/download/v${crane_version}/go-containerregistry_Linux_${file}.tar.gz" \
+ | tar -xzf - \
+ && curl -fsSL https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/4.9.9/openshift-client-linux.tar.gz \
+ | tar -xzf - -C /tmp \
+ && cp /tmp/oc .
+
+RUN chown -R root:root /usr/local/bin \
+ && chmod -R 755 /usr/local/bin
+
+############################### Application layer ##############################
+
+FROM base
+USER kloud
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+WORKDIR /workspace
+
+ENV USER=kloud
+ENV PATH=${PATH}:/home/kloud/.local/bin
+
+### Install user packages & extensions
+RUN --mount=src=src,dst=/build \
+ helm plugin install https://github.com/databus23/helm-diff \
+ && pip install --no-cache-dir -r /build/requirements.txt \
+ && pip cache purge \
+ && ansible-galaxy install -r /build/requirements.yaml \
+ && rm -rf \
+ /home/kloud/.ansible/galaxy_cache \
+ /home/kloud/.cache/helm
+
+ENTRYPOINT ["ansible-playbook"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7d1cfb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 KloudKIT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b4186aa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+# Ansible Controller
+
+> ๐ฎ Ansible controller: your configurations can stay home
+
+[](https://github.com/kloudkit/ansible-controller?tab=MIT-1-ov-file#MIT-1-ov-file)
+
+## Documentation
+
+TBD
+
+## License
+
+This project is licensed under the
+[**MIT License**](https://github.com/kloudkit/ansible-controller?tab=MIT-1-ov-file#MIT-1-ov-file)
diff --git a/renovate.json b/renovate.json
new file mode 100644
index 0000000..a7f8e49
--- /dev/null
+++ b/renovate.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended",
+ ":disableRateLimiting"
+ ],
+ "assignees": [ "bdsoha" ],
+ "labels": [ "type: dependency ๐ผ" ],
+ "schedule": [ "every weekend" ],
+ "dependencyDashboard": true,
+ "dependencyDashboardTitle": "๐ค Renovate Dashboard",
+ "commitMessageAction": "๐ผ Bump",
+ "regexManagers": [
+ {
+ "fileMatch": [ "^Dockerfile$" ],
+ "matchStrings": [
+ "# renovate: source=(?.*?) dep=(?.*?)( versioning=(?.*?))?\\nARG.*=(?.*)\\n"
+ ],
+ "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}"
+ }
+ ]
+}
diff --git a/src/add-gpg-keyrings.sh b/src/add-gpg-keyrings.sh
new file mode 100755
index 0000000..54a7b77
--- /dev/null
+++ b/src/add-gpg-keyrings.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+while IFS= read -r line; do
+ read -r name url <<< "${line}"
+
+ curl -fsSL "https://${url}" \
+ | gpg --dearmor -o "/etc/apt/keyrings/${name}.gpg"
+done < "${1}"
diff --git a/src/extra.gpg.txt b/src/extra.gpg.txt
new file mode 100644
index 0000000..8473977
--- /dev/null
+++ b/src/extra.gpg.txt
@@ -0,0 +1,2 @@
+helm baltocdn.com/helm/signing.asc
+kubernetes pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key
diff --git a/src/packages.apt b/src/packages.apt
new file mode 100644
index 0000000..84a60bd
--- /dev/null
+++ b/src/packages.apt
@@ -0,0 +1,14 @@
+curl
+dnsutils
+git
+helm
+iputils-ping
+jq
+kubectl
+locales
+netcat-traditional
+openssh-client
+python3
+python3-apt
+python3-pip
+sshpass
diff --git a/src/requirements.txt b/src/requirements.txt
new file mode 100644
index 0000000..85e46f7
--- /dev/null
+++ b/src/requirements.txt
@@ -0,0 +1,6 @@
+ansible-core==2.17.1
+docker==7.1.0
+kubernetes==30.1.0
+netaddr==1.3.0
+passlib==1.7.4
+yq==3.4.3
diff --git a/src/requirements.yaml b/src/requirements.yaml
new file mode 100644
index 0000000..61d7bea
--- /dev/null
+++ b/src/requirements.yaml
@@ -0,0 +1,13 @@
+---
+collections:
+ - name: ansible.posix
+ version: 1.5.4
+
+ - name: community.crypto
+ version: 2.20.0
+
+ - name: community.general
+ version: 9.1.0
+
+ - name: kubernetes.core
+ version: 5.0.0
diff --git a/src/rootfs/etc/ansible/ansible.cfg b/src/rootfs/etc/ansible/ansible.cfg
new file mode 100644
index 0000000..56aff87
--- /dev/null
+++ b/src/rootfs/etc/ansible/ansible.cfg
@@ -0,0 +1,9 @@
+[defaults]
+callbacks_enabled = ansible.posix.timer, community.general.dense, community.general.yaml
+callback_result_format = yaml
+fact_caching = jsonfile
+fact_caching_connection = /tmp
+force_color = True
+gathering = smart
+interpreter_python = /usr/bin/python3
+stdout_callback = community.general.yaml
diff --git a/src/rootfs/etc/ansible/hosts b/src/rootfs/etc/ansible/hosts
new file mode 100644
index 0000000..a9976c7
--- /dev/null
+++ b/src/rootfs/etc/ansible/hosts
@@ -0,0 +1,7 @@
+---
+workspace:
+ hosts:
+ localhost:
+ vars:
+ ansible_connection: local
+ ansible_become: true
diff --git a/src/rootfs/etc/apt/apt.conf.d/99controller b/src/rootfs/etc/apt/apt.conf.d/99controller
new file mode 100644
index 0000000..6e124f0
--- /dev/null
+++ b/src/rootfs/etc/apt/apt.conf.d/99controller
@@ -0,0 +1,6 @@
+APT::Install-Recommends "false";
+
+Dpkg::Options {
+ "--force-confdef";
+ "--force-confold";
+}
diff --git a/src/rootfs/etc/apt/sources.list.d/extra.sources b/src/rootfs/etc/apt/sources.list.d/extra.sources
new file mode 100644
index 0000000..1b2e4d0
--- /dev/null
+++ b/src/rootfs/etc/apt/sources.list.d/extra.sources
@@ -0,0 +1,14 @@
+##################################### helm #####################################
+
+Types: deb
+URIs: https://baltocdn.com/helm/stable/debian
+Suites: all
+Components: main
+Signed-By: /etc/apt/keyrings/helm.gpg
+
+##################################### k8s ######################################
+
+Types: deb
+URIs: https://pkgs.k8s.io/core:/stable:/v1.30/deb/
+Suites: /
+Signed-By: /etc/apt/keyrings/kubernetes.gpg
diff --git a/src/rootfs/etc/locale.gen b/src/rootfs/etc/locale.gen
new file mode 100644
index 0000000..a66d814
--- /dev/null
+++ b/src/rootfs/etc/locale.gen
@@ -0,0 +1 @@
+en_US.UTF-8 UTF-8