Compare commits

...

104 Commits

Author SHA1 Message Date
Mauro Morales
6751d7421f Add dmsetup remove before kpartx -d
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-05-24 14:48:44 +02:00
Itxaka
a868346eac Merge pull request #56 from Itxaka/keep_lvm_flag 2023-05-23 20:40:28 +02:00
Itxaka
f533e5e10e Restore --use-lvm flag for backwards compatibility
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-05-23 20:39:52 +02:00
Itxaka
83f58e301c Merge pull request #54 from Itxaka/arm_lvm_default 2023-05-23 14:38:40 +02:00
Itxaka
e95f84f0d1 Use lvm by default in building arm images
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-05-23 14:31:57 +02:00
Mauro Morales
6e4b4c901d Merge pull request #35 from kairos-io/renovate/github.com-onsi-gomega-1.x
Update module github.com/onsi/gomega to v1.27.7
2023-05-19 08:56:19 +02:00
renovate[bot]
056f142bf6 Update module github.com/onsi/gomega to v1.27.7 2023-05-18 18:57:05 +00:00
Mauro Morales
36a8aac887 Merge pull request #40 from kairos-io/renovate/actions-checkout-3.x
Update actions/checkout action to v3
2023-05-16 22:02:30 +02:00
Mauro Morales
5b792f342e Merge pull request #30 from kairos-io/renovate/golang-1.x
Update golang Docker tag to v1.20
2023-05-16 22:01:49 +02:00
Mauro Morales
446fe30aa0 Merge pull request #34 from kairos-io/renovate/github.com-onsi-ginkgo-v2-2.x
Update module github.com/onsi/ginkgo/v2 to v2.9.5
2023-05-16 22:00:27 +02:00
Mauro Morales
b2541a59bb Merge pull request #47 from kairos-io/1421-default-config-header
🐛 Add header to default config
2023-05-16 21:59:42 +02:00
Mauro Morales
f3e23108ac Add header to default config
Signed-off-by: Mauro Morales <mauro.morales@spectrocloud.com>
2023-05-16 19:27:44 +02:00
renovate[bot]
0cc7de5155 Update module github.com/onsi/ginkgo/v2 to v2.9.5 2023-05-15 16:03:51 +00:00
Itxaka
60fe50e0eb Merge pull request #41 from ci-forks/create-pull-request/patch 2023-05-12 14:07:18 +02:00
Itxaka
b3068a067c ⬆️ Update repositories
Signed-off-by: GitHub <noreply@github.com>
2023-05-12 11:55:43 +00:00
Itxaka
3b33562fc3 Merge pull request #46 from Itxaka/ignore_kpartx_failiure 2023-04-27 15:59:59 +02:00
Itxaka
30afd3535f Ignore kpartx failure on arm image
Seems that if the system is slow enough, kpartx will fail to remove the
devmapping and will fail the script

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-27 15:16:53 +02:00
Itxaka
c07dfad92f Merge pull request #45 from Itxaka/sync_before_removing_arm_image 2023-04-27 13:34:45 +02:00
Itxaka
119c25f105 Sync before trying to remove the devmappings
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-27 13:20:45 +02:00
Itxaka
2bd2a89003 Merge pull request #43 from Itxaka/fix_recovery_v3 2023-04-24 12:43:54 +02:00
Itxaka
992325101b Fix recovery size partition in raw-images script
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-24 12:16:27 +02:00
Itxaka
fb14f71fdb Merge pull request #42 from Itxaka/raw_image_recovery_suze 2023-04-18 12:44:55 +02:00
Itxaka
a1a10e08ce 🌱 Allow override of RECOVERY size in raw image creation
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-18 12:21:55 +02:00
renovate[bot]
01faaa033d Update actions/checkout action to v3 2023-04-14 08:48:09 +00:00
Itxaka
38ce6ab17b Merge pull request #32 from kairos-io/Itxaka-patch-1 2023-04-13 16:31:42 +02:00
Itxaka
16960be12e Bump repos
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-13 16:25:53 +02:00
Ettore Di Giacinto
83c61b7c5d Merge pull request #31 from kairos-io/autobump_repo
🤖 Automatically bump package repositories
2023-04-13 15:14:03 +02:00
mudler
e129ad7037 🤖 Automatically bump package repositories
Signed-off-by: mudler <mudler@c3os.io>
2023-04-13 15:04:10 +02:00
renovate[bot]
5033ac98cd Update golang Docker tag to v1.20 2023-04-13 12:56:05 +00:00
Ettore Di Giacinto
5c92f06372 Merge pull request #14 from kairos-io/renovate/configure
Configure Renovate
2023-04-13 14:55:28 +02:00
Itxaka
420d650c80 Merge pull request #28 from Itxaka/grub2_livecd_from_luet 2023-04-13 14:52:26 +02:00
Itxaka
cc4de93692 Install iso files from luet
So the proper ones per arch are installed in their dirs

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-13 14:18:52 +02:00
Itxaka
8e8e25b719 Merge pull request #27 from Itxaka/tools_elemental_from_repo 2023-04-13 12:17:58 +02:00
Itxaka
1b9b20aa20 tools: install elemental from luet repo
This makes it so arm64 osbuilder images have the proper binary arch

Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
2023-04-13 12:04:45 +02:00
renovate[bot]
7627819ea4 Add renovate.json 2023-03-09 16:24:28 +00:00
Ettore Di Giacinto
a871cfc0c1 Merge pull request #26 from kairos-io/mudler-patch-1
⬆️ Bump livecd configs
2023-03-07 12:45:17 +01:00
Ettore Di Giacinto
a4bad24fa2 ⬆️ Bump livecd configs
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
2023-03-07 12:40:14 +01:00
Venkat Srinivasan
95dd24d549 Merge pull request #25 from kairos-io/lvm-support-for-arm-images
Added optional support to build arm images with oem and recovery partitions as part of LVM
2023-02-27 08:06:48 -08:00
venkatnsrinivasan
8938adbcb9 Added optional support to build arm images with oem and recovery partitions as LVM
Signed-off-by: venkatnsrinivasan <venkat@spectrocloud.com>
2023-02-19 13:33:57 -08:00
mudler
9f5b4e7049 Build also efi.img 2023-02-18 16:10:03 +01:00
mudler
efc7cfd4e9 Add helper to pre-build static images (for ARM) 2023-02-18 14:46:54 +01:00
Ettore Di Giacinto
093850c59d 📖 Update README
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
2023-02-07 12:11:07 +01:00
Dimitris Karakasilis
724a8903fc Merge pull request #23 from kairos-io/514-produce-loadable-tar
Always produce an importable tar from the image
2023-01-09 17:10:06 +02:00
Dimitris Karakasilis
04d46465a7 Fallback to the spec name when a push image name is not provided
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2023-01-09 13:25:04 +02:00
Dimitris Karakasilis
4c8f1dd0f0 Fix typo
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-21 15:29:10 +02:00
Dimitris Karakasilis
ed201a72ef Always produce an importable tar from the image
and name it after the artifact

Fixes https://github.com/kairos-io/kairos/issues/514

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-20 10:26:10 +02:00
Ettore Di Giacinto
e6e5cddbfe Merge pull request #22 from kairos-io/515-central-server
515 central server
2022-12-15 17:25:12 +01:00
Dimitris Karakasilis
cfc4ebf308 Un-hardcode the copier image
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-15 12:29:49 +02:00
Dimitris Karakasilis
4e5e383b78 Remove not needed permission to manage services
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-15 10:42:48 +02:00
Dimitris Karakasilis
782c7caac9 Add test pipeline to PRs
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-14 16:55:25 +02:00
Dimitris Karakasilis
1346374650 Refactor kustomization, implement cleanup with finalizers and write tests
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-14 16:49:55 +02:00
Dimitris Karakasilis
224291994f [WIP] Create rbac resources to allow the Job to copy to the server Pod
Currently fails with:

```
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:hello-kairos" cannot list resource "pods" in API group "" at the cluster scope
```

because we try to list pods with `-A`. This means we are going to get a
similar error if we try to copy files to a Pod on another namespace
unless we grant permission at the cluster scope or just that
namespace. (Is that possible? Maybe if we create the Role in the same
namespace as the server.)

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-12 18:11:38 +02:00
Dimitris Karakasilis
44a48d7890 Switch from a Deployment to a Job and create permanent nginx Deployment
Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2022-12-07 17:46:19 +02:00
Ettore Di Giacinto
658b816c1d Merge pull request #21 from kairos-io/bump/cli
⬆️ Bump elemental-cli in osbuilder-tools image
2022-11-26 15:32:20 +01:00
Ettore Di Giacinto
d8a8bd1497 ⬆️ Bump elemental-cli in osbuilder-tools image
Signed-off-by: Ettore Di Giacinto <mudler@users.noreply.github.com>
2022-11-25 13:12:14 +00:00
Ettore Di Giacinto
85adc00f5a Merge pull request #18 from Ognian/master
add --efi-dir option to build-arm-iamge.sh
2022-11-18 11:12:01 +01:00
Ognian
8a9b3e9f71 cp dir content not dir
Signed-off-by: Ognian <Ognian@users.noreply.github.com>
2022-11-16 17:24:57 +01:00
Ognian
78ff22e647 add --efi-dir option to build-arm-iamge.sh
This option allows modification of raspberry pi boot options.
see https://www.raspberrypi.com/documentation/computers/config_txt.html#what-is-config-txt

Signed-off-by: Ognian <Ognian@users.noreply.github.com>
2022-11-16 12:23:56 +01:00
Ettore Di Giacinto
4522e14e32 Merge pull request #17 from Ognian/master
propagate --local to elemental pull-image in build-arm-image.sh
2022-11-14 18:32:50 +01:00
Ognian
097b4a30db Merge pull request #1 from Ognian/Ognian-patch-1
propagate --local to elemental pull-image
2022-11-14 18:21:34 +01:00
Ognian
40560a4b02 propagate --local to elemental pull-image
fixes https://github.com/kairos-io/kairos/issues/419
2022-11-14 18:19:21 +01:00
Ettore Di Giacinto
4997cf18ee Fixup duplicated crd def 2022-11-07 22:25:58 +00:00
Ettore Di Giacinto
7ae1f7105a Add option to build netboot files and disk image size 2022-11-07 22:02:32 +00:00
Ettore Di Giacinto
ebbd1c9a1a Add netboot script 2022-11-07 21:23:20 +00:00
Ettore Di Giacinto
82847a139d ⬆️ Bump elemental-cli 2022-11-07 21:50:30 +01:00
Ettore Di Giacinto
f5611e684f ⬆️ Bump elemental 2022-11-07 19:27:08 +01:00
Ettore Di Giacinto
6b7ae5af02 add bc and qemu-tools 2022-11-05 18:17:22 +00:00
Ettore Di Giacinto
ece128a0b5 Fixup artifact names 2022-11-05 16:26:15 +00:00
Ettore Di Giacinto
4b2be221b3 Add kubesplit target 2022-11-03 23:46:36 +00:00
Ettore Di Giacinto
4f87e2329c Add logic to controller to build disks 2022-11-03 23:42:09 +00:00
Ettore Di Giacinto
dc6fb2c6be Set default menu 2022-11-04 00:22:44 +01:00
Ettore Di Giacinto
dc55928694 Add scripts to generate raw images
See: https://github.com/kairos-io/kairos/issues/377
2022-11-03 22:04:50 +00:00
Ettore Di Giacinto
1f932a7644 Fixup typo 2022-10-26 17:02:05 +02:00
Ettore Di Giacinto
5c9e0a35e4 Create cloud-config dir when preparing ARM, add default config 2022-10-26 11:37:13 +00:00
Ettore Di Giacinto
a2e9c158be Add pinebookpro 2022-10-26 11:21:19 +00:00
Ettore Di Giacinto
0bfe296c53 fix typos 2022-10-24 08:42:14 +00:00
Ettore Di Giacinto
669714d915 Merge pull request #12 from santhoshdaivajna/update_elemental_luet_version
update elemental-cli and luet versions
2022-10-24 10:24:13 +02:00
Ettore Di Giacinto
5e5b3af940 Update tools-image/Dockerfile 2022-10-24 10:23:48 +02:00
Ettore Di Giacinto
ca8065f94b Make GRUBConfig and CloudConfig optional fields 2022-10-24 08:21:12 +00:00
Santhosh
8706980f2b update elemental-cli and luet versions 2022-10-20 11:50:26 +05:30
Ettore Di Giacinto
2309c0f175 🐛 correctly copy artifacts
grub2 artifacts are expected in the grub2 directory
2022-10-19 21:32:01 +02:00
Ettore Di Giacinto
086dbca453 🐛 Copy artifacts from the correct location 2022-10-19 17:24:22 +02:00
Ettore Di Giacinto
658c87a111 Use internal artifacts 2022-10-19 12:53:13 +00:00
Ettore Di Giacinto
6cafc07e65 Update Dockerfile 2022-10-16 11:43:56 +02:00
Ettore Di Giacinto
f4a4829eb0 Add ARM build script in tools-image 2022-10-15 10:01:42 +00:00
Ettore Di Giacinto
92749af928 Add helper to rebake iso with config 2022-10-13 10:37:51 +00:00
Ettore Di Giacinto
44e07e8218 ⬆️ Bump osbuilder packages 2022-10-11 22:41:05 +02:00
Ettore Di Giacinto
4dcfcc4172 Update README.md 2022-09-23 23:07:09 +02:00
mudler
9ce274af56 Use artifacts from the kairos repository in the osbuilder image 2022-09-22 00:04:55 +02:00
Ettore Di Giacinto
ac60a3dc91 🤖 Fixup test 2022-09-19 10:16:50 +00:00
Ettore Di Giacinto
93769cf295 🎨 Rebrand to Kairos
See: https://github.com/kairos-io/kairos/issues/88
2022-09-19 10:06:14 +00:00
Ettore Di Giacinto
7e4846785d Use one image with tools 2022-09-13 21:55:53 +00:00
Ettore Di Giacinto
cb21b7742b Add luet to tool image 2022-09-13 21:54:32 +00:00
Ettore Di Giacinto
6dde050079 Pass ginkgo bin location 2022-09-13 21:24:07 +00:00
Ettore Di Giacinto
ad3860dbd3 Hook ginkgo to e2e test run 2022-09-13 21:18:27 +00:00
Ettore Di Giacinto
3bc7583466 Handle from makefile 2022-09-13 21:10:01 +00:00
Ettore Di Giacinto
6951d26fda Install ginkgo before calling tests 2022-09-13 21:04:36 +00:00
Ettore Di Giacinto
da695e3461 Fixup dockerfile location 2022-09-13 21:03:16 +00:00
Ettore Di Giacinto
287ed276eb Use tools image in the controller 2022-09-13 20:49:18 +00:00
Ettore Di Giacinto
29da8c271e Add tool-image 2022-09-13 20:41:45 +00:00
Ettore Di Giacinto
2997c7aa2f 🤖 Add e2e test workflow 2022-09-10 22:45:28 +00:00
Ettore Di Giacinto
78a728910d Default to nodeport 2022-09-10 22:15:40 +00:00
Ettore Di Giacinto
680907ab43 Add osRelease field 2022-09-09 15:09:20 +00:00
mudler
b3e7dcbf59 Add support for bundles and custom grub config 2022-09-06 22:27:15 +02:00
63 changed files with 2432 additions and 577 deletions

26
.github/workflows/bump_repos.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Bump repositories
on:
schedule:
- cron: 0 20 * * *
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install earthly
uses: Luet-lab/luet-install-action@v1
with:
repository: quay.io/kairos/packages
packages: utils/earthly
- name: Bump cos 🔧
run: earthly +bump-repositories
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.PAT_TOKEN }}
push-to-fork: ci-forks/osbuilder
commit-message: ':arrow_up: Update repositories'
title: ':arrow_up: Update repositories'
body: Bump of Kairos repositories
signoff: true

View File

@@ -13,12 +13,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=quay.io/c3os/osbuilder
DOCKER_IMAGE=quay.io/kairos/osbuilder
VERSION=latest
SHORTREF=${GITHUB_SHA::8}
# If this is git tag, use the tag name as a docker tag

20
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
---
name: 'test'
on:
push:
branches:
- master
tags:
- '*'
pull_request:
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Test
run: |
make kind-e2e-tests

63
.github/workflows/tool-image.yml vendored Normal file
View File

@@ -0,0 +1,63 @@
---
name: 'build tools container images'
on:
push:
branches:
- master
tags:
- '*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Prepare
id: prep
run: |
DOCKER_IMAGE=quay.io/kairos/osbuilder-tools
VERSION=latest
SHORTREF=${GITHUB_SHA::8}
# If this is git tag, use the tag name as a docker tag
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/}
fi
TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:${SHORTREF}"
# If the VERSION looks like a version number, assume that
# this is the most recent version of the image and also
# tag it 'latest'.
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
# Set output parameters.
echo ::set-output name=tags::${TAGS}
echo ::set-output name=docker_image::${DOCKER_IMAGE}
- name: Set up QEMU
uses: docker/setup-qemu-action@master
with:
platforms: all
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v1
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- name: Build
uses: docker/build-push-action@v2
with:
builder: ${{ steps.buildx.outputs.name }}
context: ./tools-image
file: ./tools-image/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}

View File

@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.18 as builder
FROM golang:1.20 as builder
WORKDIR /workspace
# Copy the Go Modules manifests

22
Earthfile Normal file
View File

@@ -0,0 +1,22 @@
VERSION 0.6
last-commit-packages:
FROM quay.io/skopeo/stable
RUN dnf install -y jq
WORKDIR build
RUN skopeo list-tags docker://quay.io/kairos/packages | jq -rc '.Tags | map(select( (. | contains("-repository.yaml")) )) | sort_by(. | sub("v";"") | sub("-repository.yaml";"") | sub("-";"") | split(".") | map(tonumber) ) | .[-1]' > REPO_AMD64
RUN skopeo list-tags docker://quay.io/kairos/packages-arm64 | jq -rc '.Tags | map(select( (. | contains("-repository.yaml")) )) | sort_by(. | sub("v";"") | sub("-repository.yaml";"") | sub("-";"") | split(".") | map(tonumber) ) | .[-1]' > REPO_ARM64
SAVE ARTIFACT REPO_AMD64 REPO_AMD64
SAVE ARTIFACT REPO_ARM64 REPO_ARM64
bump-repositories:
FROM mikefarah/yq
WORKDIR build
COPY +last-commit-packages/REPO_AMD64 REPO_AMD64
COPY +last-commit-packages/REPO_ARM64 REPO_ARM64
ARG REPO_AMD64=$(cat REPO_AMD64)
ARG REPO_ARM64=$(cat REPO_ARM64)
COPY tools-image/luet.yaml luet.yaml
RUN yq eval ".repositories[0] |= . * { \"reference\": \"${REPO_AMD64}\" }" -i luet.yaml
RUN yq eval ".repositories[1] |= . * { \"reference\": \"${REPO_ARM64}\" }" -i luet.yaml
SAVE ARTIFACT luet.yaml AS LOCAL tools-image/luet.yaml

View File

@@ -4,8 +4,8 @@
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
VERSION ?= 0.0.1
IMG ?= quay.io/c3os/osbuilder:test
CLUSTER_NAME?="c3os-osbuilder-e2e"
IMG ?= quay.io/kairos/osbuilder:test
CLUSTER_NAME?="kairos-osbuilder-e2e"
export ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
# CHANNELS define the bundle channels used in the bundle.
@@ -31,8 +31,8 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
# This variable is used to construct full image tags for bundle and catalog images.
#
# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
# c3os-x.io/osbuilder-operator-bundle:$VERSION and c3os-x.io/osbuilder-operator-catalog:$VERSION.
IMAGE_TAG_BASE ?= c3os-x.io/osbuilder-operator
# kairos.io/osbuilder-bundle:$VERSION and kairos.io/osbuilder-catalog:$VERSION.
IMAGE_TAG_BASE ?= kairos.io/osbuilder
# BUNDLE_IMG defines the image:tag used for the bundle.
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
@@ -147,13 +147,12 @@ uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified
.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
# TODO: No need to build and then apply. `kubectl apply -k config/default` does the trick
$(KUSTOMIZE) build config/default | kubectl apply -f -
.PHONY: deploy-dev
deploy-dev: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/dev | kubectl apply -f -
kubectl apply -k config/dev
.PHONY: undeploy
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
@@ -172,6 +171,7 @@ $(LOCALBIN):
## Tool Binaries
KUSTOMIZE ?= $(LOCALBIN)/kustomize
GINKGO ?= $(LOCALBIN)/ginkgo
CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
@@ -190,6 +190,10 @@ controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessar
$(CONTROLLER_GEN): $(LOCALBIN)
GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION)
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.
$(GINKGO): $(LOCALBIN)
GOBIN=$(LOCALBIN) go install -mod=mod github.com/onsi/ginkgo/v2/ginkgo
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
$(ENVTEST): $(LOCALBIN)
@@ -266,9 +270,15 @@ test_deps:
.PHONY: unit-tests
unit-tests: test_deps
ginkgo -r -v --covermode=atomic --coverprofile=coverage.out -p -r ./pkg/...
$(GINKGO) -r -v --covermode=atomic --coverprofile=coverage.out -p -r ./pkg/...
e2e-tests:
KUBE_VERSION=${KUBE_VERSION} $(ROOT_DIR)/script/test.sh
GINKGO=$(GINKGO) KUBE_VERSION=${KUBE_VERSION} $(ROOT_DIR)/script/test.sh
kind-e2e-tests: kind-setup install undeploy-dev deploy-dev e2e-tests
kind-e2e-tests: ginkgo kind-setup install undeploy-dev deploy-dev e2e-tests
kubesplit: manifests kustomize
rm -rf helm-chart
mkdir helm-chart
$(KUSTOMIZE) build config/default | kubesplit -helm helm-chart

View File

@@ -1,19 +1,19 @@
domain: c3os-x.io
domain: kairos.io
layout:
- go.kubebuilder.io/v3
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
projectName: osartifactbuilder-operator
repo: github.com/c3os-io/osbuilder-operator
repo: github.com/kairos-io/osbuilder
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: c3os-x.io
domain: kairos.io
group: build
kind: OSArtifact
path: github.com/c3os-io/osbuilder-operator/api/v1alpha1
path: github.com/kairos-io/osbuilder/api/v1alpha1
version: v1alpha1
version: "3"

169
README.md
View File

@@ -1,94 +1,87 @@
# osartifactbuilder-operator
// TODO(user): Add simple overview of use/purpose
<h1 align="center">
<br>
<img width="184" alt="kairos-white-column 5bc2fe34" src="https://user-images.githubusercontent.com/2420543/193010398-72d4ba6e-7efe-4c2e-b7ba-d3a826a55b7d.png"><br>
osbuilder
<br>
</h1>
## Description
// TODO(user): An in-depth paragraph about your project and overview of use
<h3 align="center">Kubernetes Native Extension to build Linux distributions artifacts from container images</h3>
<p align="center">
<a href="https://opensource.org/licenses/">
<img src="https://img.shields.io/badge/licence-APL2-brightgreen"
alt="license">
</a>
<a href="https://github.com/kairos-io/osbuilder/issues"><img src="https://img.shields.io/github/issues/kairos-io/osbuilder"></a>
<a href="https://kairos.io/docs/" target=_blank> <img src="https://img.shields.io/badge/Documentation-blue"
alt="docs"></a>
<img src="https://img.shields.io/badge/made%20with-Go-blue">
<img src="https://goreportcard.com/badge/github.com/kairos-io/osbuilder" alt="go report card" />
</p>
## Getting Started
Youll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.
**Note:** Your controller will automatically use the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows).
### Running on the cluster
1. Install Instances of Custom Resources:
With Kairos you can build immutable, bootable Kubernetes and OS images for your edge devices as easily as writing a Dockerfile. Optional P2P mesh with distributed ledger automates node bootstrapping and coordination. Updating nodes is as easy as CI/CD: push a new image to your container registry and let secure, risk-free A/B atomic upgrades do the rest.
```sh
kubectl apply -f config/samples/
<table>
<tr>
<th align="center">
<img width="640" height="1px">
<p>
<small>
Documentation
</small>
</p>
</th>
<th align="center">
<img width="640" height="1">
<p>
<small>
Contribute
</small>
</p>
</th>
</tr>
<tr>
<td>
📚 [Getting started with Kairos](https://kairos.io/docs/getting-started) <br> :bulb: [Examples](https://kairos.io/docs/examples) <br> :movie_camera: [Video](https://kairos.io/docs/media/) <br> :open_hands:[Engage with the Community](https://kairos.io/community/)
</td>
<td>
🙌[ CONTRIBUTING.md ]( https://github.com/kairos-io/kairos/blob/master/CONTRIBUTING.md ) <br> :raising_hand: [ GOVERNANCE ]( https://github.com/kairos-io/kairos/blob/master/GOVERNANCE.md ) <br>:construction_worker:[Code of conduct](https://github.com/kairos-io/kairos/blob/master/CODE_OF_CONDUCT.md)
</td>
</tr>
</table>
| :exclamation: | This is experimental! |
|-|:-|
## Installation
To install, use helm:
```
# Adds the kairos repo to helm
$ helm repo add kairos https://kairos-io.github.io/helm-charts
"kairos" has been added to your repositories
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "kairos" chart repository
Update Complete. ⎈Happy Helming!⎈
# Install the CRD chart
$ helm install kairos-crd kairos/kairos-crds
NAME: kairos-crd
LAST DEPLOYED: Tue Sep 6 20:35:34 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
# Installs osbuilder
$ helm install kairos-osbuilder kairos/osbuilder
```
2. Build and push your image to the location specified by `IMG`:
```sh
make docker-build docker-push IMG=<some-registry>/osartifactbuilder-operator:tag
```
3. Deploy the controller to the cluster with the image specified by `IMG`:
```sh
make deploy IMG=<some-registry>/osartifactbuilder-operator:tag
```
### Uninstall CRDs
To delete the CRDs from the cluster:
```sh
make uninstall
```
### Undeploy controller
UnDeploy the controller to the cluster:
```sh
make undeploy
```
## Contributing
// TODO(user): Add detailed information on how you would like others to contribute to this project
### How it works
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/)
which provides a reconcile function responsible for synchronizing resources untile the desired state is reached on the cluster
### Test It Out
1. Install the CRDs into the cluster:
```sh
make install
```
2. Run your controller (this will run in the foreground, so switch to a new terminal if you want to leave it running):
```sh
make run
```
**NOTE:** You can also run this in one step by running: `make install run`
### Modifying the API definitions
If you are editing the API definitions, generate the manifests such as CRs or CRDs using:
```sh
make manifests
```
**NOTE:** Run `make --help` for more information on all potential `make` targets
More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
## License
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Documentation available at: https://kairos.io/docs/advanced/build/

View File

@@ -15,8 +15,8 @@ limitations under the License.
*/
// Package v1alpha1 contains API Schema definitions for the build v1alpha1 API group
//+kubebuilder:object:generate=true
//+groupName=build.c3os-x.io
// +kubebuilder:object:generate=true
// +groupName=build.kairos.io
package v1alpha1
import (
@@ -26,7 +26,7 @@ import (
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "build.c3os-x.io", Version: "v1alpha1"}
GroupVersion = schema.GroupVersion{Group: "build.kairos.io", Version: "v1alpha1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

View File

@@ -30,12 +30,48 @@ type OSArtifactSpec struct {
// Foo is an example field of OSArtifact. Edit osartifact_types.go to remove/update
ImageName string `json:"imageName,omitempty"`
ISO bool `json:"iso,omitempty"`
// This needs to be revisited
ISO bool `json:"iso,omitempty"`
//Disk-only stuff
DiskSize string `json:"diskSize,omitempty"`
CloudImage bool `json:"cloudImage,omitempty"`
AzureImage bool `json:"azureImage,omitempty"`
GCEImage bool `json:"gceImage,omitempty"`
Netboot bool `json:"netboot,omitempty"`
NetbootURL string `json:"netbootURL,omitempty"`
// TODO: treat cloudconfig as a secret, and take a secretRef where to store it (optionally)
CloudConfig string `json:"cloudConfig,omitempty"`
GRUBConfig string `json:"grubConfig,omitempty"`
PullFromKube bool `json:"pullFromKube,omitempty"`
Bundles []string `json:"bundles,omitempty"`
PullOptions Pull `json:"pull,omitempty"`
OSRelease string `json:"osRelease,omitempty"`
// TODO: Currently not used. Reserved to be used when we have a way to push to registries.
PushOptions Push `json:"push,omitempty"`
}
type Push struct {
Push bool `json:"push,omitempty"`
ImageName string `json:"imageName,omitempty"`
ContainerRegistryCredentials *SecretKeySelector `json:"containerRegistryCredentials,omitempty"`
}
type Pull struct {
ContainerRegistryCredentials *SecretKeySelector `json:"containerRegistryCredentials,omitempty"`
}
type LocalObjectReference struct {
Name string `json:"name"`
}
type SecretKeySelector struct {
LocalObjectReference `json:",inline"`
// +optional
Namespace string `json:"namespace,omitempty"`
// +optional
Key string `json:"key,omitempty"`
}
// OSArtifactStatus defines the observed state of OSArtifact

View File

@@ -25,12 +25,27 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference.
func (in *LocalObjectReference) DeepCopy() *LocalObjectReference {
if in == nil {
return nil
}
out := new(LocalObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OSArtifact) DeepCopyInto(out *OSArtifact) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
@@ -87,6 +102,13 @@ func (in *OSArtifactList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OSArtifactSpec) DeepCopyInto(out *OSArtifactSpec) {
*out = *in
if in.Bundles != nil {
in, out := &in.Bundles, &out.Bundles
*out = make([]string, len(*in))
copy(*out, *in)
}
in.PullOptions.DeepCopyInto(&out.PullOptions)
in.PushOptions.DeepCopyInto(&out.PushOptions)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OSArtifactSpec.
@@ -113,3 +135,59 @@ func (in *OSArtifactStatus) DeepCopy() *OSArtifactStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Pull) DeepCopyInto(out *Pull) {
*out = *in
if in.ContainerRegistryCredentials != nil {
in, out := &in.ContainerRegistryCredentials, &out.ContainerRegistryCredentials
*out = new(SecretKeySelector)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Pull.
func (in *Pull) DeepCopy() *Pull {
if in == nil {
return nil
}
out := new(Pull)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Push) DeepCopyInto(out *Push) {
*out = *in
if in.ContainerRegistryCredentials != nil {
in, out := &in.ContainerRegistryCredentials, &out.ContainerRegistryCredentials
*out = new(SecretKeySelector)
**out = **in
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Push.
func (in *Push) DeepCopy() *Push {
if in == nil {
return nil
}
out := new(Push)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
out.LocalObjectReference = in.LocalObjectReference
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeySelector.
func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
if in == nil {
return nil
}
out := new(SecretKeySelector)
in.DeepCopyInto(out)
return out
}

View File

@@ -5,9 +5,9 @@ metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.9.0
creationTimestamp: null
name: osartifacts.build.c3os-x.io
name: osartifacts.build.kairos.io
spec:
group: build.c3os-x.io
group: build.kairos.io
names:
kind: OSArtifact
listKind: OSArtifactList
@@ -35,14 +35,70 @@ spec:
spec:
description: OSArtifactSpec defines the desired state of OSArtifact
properties:
azureImage:
type: boolean
bundles:
items:
type: string
type: array
cloudConfig:
description: 'TODO: treat cloudconfig as a secret, and take a secretRef
where to store it (optionally)'
type: string
cloudImage:
type: boolean
diskSize:
description: Disk-only stuff
type: string
gceImage:
type: boolean
grubConfig:
type: string
imageName:
description: Foo is an example field of OSArtifact. Edit osartifact_types.go
to remove/update
type: string
iso:
description: This needs to be revisited
type: boolean
netboot:
type: boolean
netbootURL:
type: string
osRelease:
type: string
pull:
properties:
containerRegistryCredentials:
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- name
type: object
type: object
push:
properties:
containerRegistryCredentials:
properties:
key:
type: string
name:
type: string
namespace:
type: string
required:
- name
type: object
imageName:
type: string
push:
type: boolean
type: object
type: object
status:
description: OSArtifactStatus defines the observed state of OSArtifact

View File

@@ -2,7 +2,7 @@
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/build.c3os-x.io_osartifacts.yaml
- bases/build.kairos.io_osartifacts.yaml
#+kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:

View File

@@ -4,4 +4,4 @@ kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: osartifacts.build.c3os-x.io
name: osartifacts.build.kairos.io

View File

@@ -2,7 +2,7 @@
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: osartifacts.build.c3os-x.io
name: osartifacts.build.kairos.io
spec:
conversion:
strategy: Webhook

View File

@@ -16,6 +16,7 @@ bases:
- ../crd
- ../rbac
- ../manager
- ../nginx
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
@@ -72,3 +73,16 @@ vars:
# kind: Service
# version: v1
# name: webhook-service
vars:
- name: NGINX_NAMESPACE
objref:
kind: Namespace
name: system
apiVersion: v1
- name: ARTIFACT_COPIER_ROLE
objref:
kind: Role
name: artifactCopier
apiVersion: rbac.authorization.k8s.io/v1

View File

@@ -38,3 +38,7 @@ spec:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"
- "--copy-to-namespace=$(NGINX_NAMESPACE)"
- "--copy-role=$(ARTIFACT_COPIER_ROLE)"
- --copy-to-pod-label=app.kubernetes.io/name=osbuilder-nginx
- --copy-to-path="/usr/share/nginx/html"

View File

@@ -1,74 +1,7 @@
# Adds namespace to all resources.
namespace: osartifactbuilder-operator-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: osartifactbuilder-operator-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
bases:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
#- ../prometheus
- ../default
patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
- manager_auth_proxy_patch.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type
#- manager_config_patch.yaml
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- manager_webhook_patch.yaml
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
#- webhookcainjection_patch.yaml
# the following config is for teaching kustomize how to do var substitution
vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service
images:
- name: quay.io/kairos/osbuilder
newName: quay.io/kairos/osbuilder
newTag: test

View File

@@ -1,40 +0,0 @@
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
securityContext:
allowPrivilegeEscalation: false
# TODO(user): uncomment for common cases that do not require escalating privileges
# capabilities:
# drop:
# - "ALL"
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.11.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=0"
ports:
- containerPort: 8443
protocol: TCP
name: https
resources:
limits:
cpu: 500m
memory: 128Mi
requests:
cpu: 5m
memory: 64Mi
- name: manager
args:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"

View File

@@ -1,21 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
imagePullPolicy: Never
args:
- "--config=controller_manager_config.yaml"
volumeMounts:
- name: manager-config
mountPath: /controller_manager_config.yaml
subPath: controller_manager_config.yaml
volumes:
- name: manager-config
configMap:
name: manager-config

View File

@@ -8,7 +8,7 @@ webhook:
port: 9443
leaderElection:
leaderElect: true
resourceName: 98ca89ca.c3os-x.io
resourceName: 98ca89ca.kairos.io
# leaderElectionReleaseOnCancel defines if the leader should step down volume
# when the Manager ends. This requires the binary to immediately end when the
# Manager is stopped, otherwise, this setting is unsafe. Setting this significantly

View File

@@ -1,6 +1,8 @@
resources:
- manager.yaml
namespace: system
generatorOptions:
disableNameSuffixHash: true
@@ -12,5 +14,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: quay.io/c3os/osbuilder
newName: quay.io/kairos/osbuilder
newTag: test

View File

@@ -9,7 +9,6 @@ apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
labels:
control-plane: controller-manager
spec:

View File

@@ -0,0 +1,41 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-public
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app.kubernetes.io/name: osbuilder-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: osbuilder-nginx
replicas: 1
template:
metadata:
labels:
app.kubernetes.io/name: osbuilder-nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-public
ports:
- containerPort: 80
serviceAccountName: controller-manager
terminationGracePeriodSeconds: 10
volumes:
- name: nginx-public
persistentVolumeClaim:
claimName: nginx-public

View File

@@ -0,0 +1,4 @@
resources:
- deployment.yaml
- service.yaml
- role.yaml

18
config/nginx/role.yaml Normal file
View File

@@ -0,0 +1,18 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: artifactCopier
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- get
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create

12
config/nginx/service.yaml Normal file
View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: osbuilder-nginx
spec:
type: NodePort
selector:
app.kubernetes.io/name: osbuilder-nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

View File

@@ -5,7 +5,7 @@ metadata:
name: osartifact-editor-role
rules:
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts
verbs:
@@ -17,7 +17,7 @@ rules:
- update
- watch
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/status
verbs:

View File

@@ -5,7 +5,7 @@ metadata:
name: osartifact-viewer-role
rules:
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts
verbs:
@@ -13,7 +13,7 @@ rules:
- list
- watch
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/status
verbs:

View File

@@ -6,7 +6,7 @@ metadata:
name: manager-role
rules:
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts
verbs:
@@ -18,13 +18,13 @@ rules:
- update
- watch
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/finalizers
verbs:
- update
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/status
verbs:

View File

@@ -6,7 +6,32 @@ metadata:
name: manager-role
rules:
- apiGroups:
- build.c3os-x.io
- ""
resources:
- serviceaccounts
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- "rbac.authorization.k8s.io"
resources:
- roles
- rolebindings
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- build.kairos.io
resources:
- osartifacts
verbs:
@@ -18,13 +43,13 @@ rules:
- update
- watch
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/finalizers
verbs:
- update
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/status
verbs:
@@ -32,7 +57,7 @@ rules:
- patch
- update
- apiGroups:
- build.c3os-x.io
- build.kairos.io
resources:
- osartifacts/finalizers
verbs:
@@ -40,17 +65,30 @@ rules:
- apiGroups:
- ""
resources:
- services
- configmaps
verbs:
- get
- create
- update
- apiGroups:
- "apps"
- "batch"
resources:
- deployments
- jobs
verbs:
- get
- create
- update
# Temporary so that it can grant these permissions to the created role
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- get
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create

View File

@@ -1,4 +1,4 @@
apiVersion: build.c3os-x.io/v1alpha1
apiVersion: build.kairos.io/v1alpha1
kind: OSArtifact
metadata:
name: osartifact-sample

View File

@@ -17,7 +17,7 @@ limitations under the License.
package controllers
import (
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -32,7 +32,8 @@ func (r *OSArtifactReconciler) genConfigMap(artifact buildv1alpha1.OSArtifact) *
OwnerReferences: genOwner(artifact),
},
Data: map[string]string{
"config": artifact.Spec.CloudConfig,
"grub.cfg": artifact.Spec.GRUBConfig,
"config": artifact.Spec.CloudConfig,
"grub.cfg": artifact.Spec.GRUBConfig,
"os-release": artifact.Spec.OSRelease,
}}
}

View File

@@ -1,161 +0,0 @@
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"fmt"
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func genDeploymentLabel(s string) map[string]string {
return map[string]string{
"osbuild": "workload" + s,
}
}
func (r *OSArtifactReconciler) genDeployment(artifact buildv1alpha1.OSArtifact) *appsv1.Deployment {
objMeta := metav1.ObjectMeta{
Name: artifact.Name,
Namespace: artifact.Namespace,
OwnerReferences: genOwner(artifact),
}
privileged := false
serviceAccount := false
buildIsoContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-iso",
Image: r.BuildImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"elemental --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay %s --output /public",
artifact.Name,
artifact.Spec.ImageName,
),
},
VolumeMounts: []v1.VolumeMount{
{
Name: "public",
MountPath: "/public",
},
{
Name: "config",
MountPath: "/iso/iso-overlay/cloud_config.yaml",
SubPath: "config",
},
{
Name: "grub",
MountPath: "/iso/iso-overlay/boot/grub2/grub.cfg",
SubPath: "grub.cfg",
},
},
}
if artifact.Spec.PullFromKube {
buildIsoContainer.Args = []string{
fmt.Sprintf(
"elemental --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /public /rootfs",
artifact.Name,
),
}
}
pullContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
Name: "build-iso",
Image: artifact.Spec.ImageName,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"rsync -aqAX --exclude='mnt' --exclude='proc' --exclude='sys' --exclude='dev' --exclude='tmp' %s %s",
"/",
"/rootfs",
),
},
VolumeMounts: []v1.VolumeMount{
{
Name: "rootfs",
MountPath: "/rootfs",
},
},
}
servingContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "serve",
Ports: []v1.ContainerPort{v1.ContainerPort{Name: "http", ContainerPort: 80}},
Image: r.ServingImage,
VolumeMounts: []v1.VolumeMount{
{
Name: "public",
MountPath: "/usr/share/nginx/html",
},
},
}
pod := v1.PodSpec{
AutomountServiceAccountToken: &serviceAccount,
Volumes: []v1.Volume{
{
Name: "public",
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
},
{
Name: "rootfs",
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
},
{
Name: "config",
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{
LocalObjectReference: v1.LocalObjectReference{Name: artifact.Name}}},
},
},
}
pod.InitContainers = []v1.Container{buildIsoContainer}
if artifact.Spec.PullFromKube {
// pull first
pod.InitContainers = append([]v1.Container{pullContainer}, pod.InitContainers...)
}
pod.Containers = []v1.Container{servingContainer}
deploymentLabels := genDeploymentLabel(artifact.Name)
replicas := int32(1)
return &appsv1.Deployment{
ObjectMeta: objMeta,
Spec: appsv1.DeploymentSpec{
Selector: &metav1.LabelSelector{MatchLabels: deploymentLabels},
Replicas: &replicas,
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: deploymentLabels,
},
Spec: pod,
},
},
}
}

533
controllers/job.go Normal file
View File

@@ -0,0 +1,533 @@
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"bytes"
"context"
"fmt"
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
"github.com/pkg/errors"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/remotecommand"
)
func genJobLabel(s string) map[string]string {
return map[string]string{
"osbuild": "workload" + s,
}
}
// TODO: Handle registry auth
// TODO: This shells out, but needs ENV_VAR with key refs mapping
// TODO: Cache downloaded images?
func unpackContainer(id, containerImage, pullImage string, pullOptions buildv1alpha1.Pull) v1.Container {
return v1.Container{
ImagePullPolicy: v1.PullAlways,
Name: fmt.Sprintf("pull-image-%s", id),
Image: containerImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"luet util unpack %s %s",
pullImage,
"/rootfs",
),
},
VolumeMounts: []v1.VolumeMount{
{
Name: "rootfs",
MountPath: "/rootfs",
},
},
}
}
func pushImageName(artifact buildv1alpha1.OSArtifact) string {
pushName := artifact.Spec.PushOptions.ImageName
if pushName != "" {
return pushName
}
return artifact.Name
}
func createImageContainer(containerImage string, artifact buildv1alpha1.OSArtifact) v1.Container {
imageName := pushImageName(artifact)
return v1.Container{
ImagePullPolicy: v1.PullAlways,
Name: "create-image",
Image: containerImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"tar -czvpf test.tar -C /rootfs . && luet util pack %[1]s test.tar %[2]s.tar && chmod +r %[2]s.tar && mv %[2]s.tar /artifacts",
imageName,
artifact.Name,
),
},
VolumeMounts: []v1.VolumeMount{
{
Name: "rootfs",
MountPath: "/rootfs",
},
{
Name: "artifacts",
MountPath: "/artifacts",
},
},
}
}
func createPushToServerImageContainer(containerImage string, artifactPodInfo ArtifactPodInfo) v1.Container {
command := fmt.Sprintf("tar cf - -C artifacts/ . | kubectl exec -i -n %s $(kubectl get pods -l %s -n %s --no-headers -o custom-columns=\":metadata.name\" | head -n1) -- tar xf - -C %s", artifactPodInfo.Namespace, artifactPodInfo.Label, artifactPodInfo.Namespace, artifactPodInfo.Path)
fmt.Printf("command = %+v\n", command)
return v1.Container{
ImagePullPolicy: v1.PullAlways,
Name: "push-to-server",
Image: containerImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{command},
VolumeMounts: []v1.VolumeMount{
{
Name: "rootfs",
MountPath: "/rootfs",
},
{
Name: "artifacts",
MountPath: "/artifacts",
},
},
}
}
func osReleaseContainer(containerImage string) v1.Container {
return v1.Container{
ImagePullPolicy: v1.PullAlways,
Name: "os-release",
Image: containerImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
"cp -rfv /etc/os-release /rootfs/etc/os-release",
},
VolumeMounts: []v1.VolumeMount{
{
Name: "config",
MountPath: "/etc/os-release",
SubPath: "os-release",
},
{
Name: "rootfs",
MountPath: "/rootfs",
},
},
}
}
func (r *OSArtifactReconciler) genJob(artifact buildv1alpha1.OSArtifact) *batchv1.Job {
objMeta := genObjectMeta(artifact)
privileged := false
serviceAccount := true
cmd := fmt.Sprintf(
"/entrypoint.sh --debug --name %s build-iso --date=false --output /artifacts dir:/rootfs",
artifact.Name,
)
volumeMounts := []v1.VolumeMount{
{
Name: "artifacts",
MountPath: "/artifacts",
},
{
Name: "rootfs",
MountPath: "/rootfs",
},
}
if artifact.Spec.GRUBConfig != "" {
volumeMounts = append(volumeMounts, v1.VolumeMount{
Name: "config",
MountPath: "/iso/iso-overlay/boot/grub2/grub.cfg",
SubPath: "grub.cfg",
})
}
cloudImgCmd := fmt.Sprintf(
"/raw-images.sh /rootfs /artifacts/%s.raw",
artifact.Name,
)
if artifact.Spec.CloudConfig != "" {
volumeMounts = append(volumeMounts, v1.VolumeMount{
Name: "config",
MountPath: "/iso/iso-overlay/cloud_config.yaml",
SubPath: "config",
})
cloudImgCmd += " /iso/iso-overlay/cloud_config.yaml"
}
if artifact.Spec.CloudConfig != "" || artifact.Spec.GRUBConfig != "" {
cmd = fmt.Sprintf(
"/entrypoint.sh --debug --name %s build-iso --date=false --overlay-iso /iso/iso-overlay --output /artifacts dir:/rootfs",
artifact.Name,
)
}
buildIsoContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-iso",
Image: r.ToolImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
cmd,
},
VolumeMounts: volumeMounts,
}
buildCloudImageContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-cloud-image",
Image: r.ToolImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
cloudImgCmd,
},
VolumeMounts: volumeMounts,
}
if artifact.Spec.DiskSize != "" {
buildCloudImageContainer.Env = []v1.EnvVar{{
Name: "EXTEND",
Value: artifact.Spec.DiskSize,
}}
}
extractNetboot := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-netboot",
Image: r.ToolImage,
Command: []string{"/bin/bash", "-cxe"},
Env: []v1.EnvVar{{
Name: "URL",
Value: artifact.Spec.NetbootURL,
}},
Args: []string{
fmt.Sprintf(
"/netboot.sh /artifacts/%s.iso /artifacts/%s",
artifact.Name,
artifact.Name,
),
},
VolumeMounts: volumeMounts,
}
buildAzureCloudImageContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-azure-cloud-image",
Image: r.ToolImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"/azure.sh /artifacts/%s.raw /artifacts/%s.vhd",
artifact.Name,
artifact.Name,
),
},
VolumeMounts: volumeMounts,
}
buildGCECloudImageContainer := v1.Container{
ImagePullPolicy: v1.PullAlways,
SecurityContext: &v1.SecurityContext{Privileged: &privileged},
Name: "build-gce-cloud-image",
Image: r.ToolImage,
Command: []string{"/bin/bash", "-cxe"},
Args: []string{
fmt.Sprintf(
"/gce.sh /artifacts/%s.raw /artifacts/%s.gce.raw",
artifact.Name,
artifact.Name,
),
},
VolumeMounts: volumeMounts,
}
pod := v1.PodSpec{
AutomountServiceAccountToken: &serviceAccount,
ServiceAccountName: objMeta.Name,
RestartPolicy: v1.RestartPolicyNever,
Volumes: []v1.Volume{
{
Name: "artifacts",
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
},
{
Name: "rootfs",
VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}},
},
{
Name: "config",
VolumeSource: v1.VolumeSource{
ConfigMap: &v1.ConfigMapVolumeSource{
LocalObjectReference: v1.LocalObjectReference{Name: artifact.Name}}},
},
},
}
pod.InitContainers = []v1.Container{unpackContainer("baseimage", r.ToolImage, artifact.Spec.ImageName, artifact.Spec.PullOptions)}
for i, bundle := range artifact.Spec.Bundles {
pod.InitContainers = append(pod.InitContainers, unpackContainer(fmt.Sprint(i), r.ToolImage, bundle, artifact.Spec.PullOptions))
}
if artifact.Spec.OSRelease != "" {
pod.InitContainers = append(pod.InitContainers, osReleaseContainer(r.ToolImage))
}
if artifact.Spec.ISO || artifact.Spec.Netboot {
pod.InitContainers = append(pod.InitContainers, buildIsoContainer)
}
if artifact.Spec.Netboot {
pod.InitContainers = append(pod.InitContainers, extractNetboot)
}
if artifact.Spec.CloudImage || artifact.Spec.AzureImage || artifact.Spec.GCEImage {
pod.InitContainers = append(pod.InitContainers, buildCloudImageContainer)
}
if artifact.Spec.AzureImage {
pod.InitContainers = append(pod.InitContainers, buildAzureCloudImageContainer)
}
if artifact.Spec.GCEImage {
pod.InitContainers = append(pod.InitContainers, buildGCECloudImageContainer)
}
pod.InitContainers = append(pod.InitContainers, createImageContainer(r.ToolImage, artifact))
pod.Containers = []v1.Container{
createPushToServerImageContainer(r.CopierImage, r.ArtifactPodInfo),
}
jobLabels := genJobLabel(artifact.Name)
job := batchv1.Job{
ObjectMeta: objMeta,
Spec: batchv1.JobSpec{
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: jobLabels,
},
Spec: pod,
},
},
}
return &job
}
// createServiceAccount creates a service account that has the permissions to
// copy the artifacts to the http server Pod. This service account is used for
// the "push to server" container.
func (r *OSArtifactReconciler) createCopierServiceAccount(ctx context.Context, objMeta metav1.ObjectMeta) error {
sa, err := r.clientSet.CoreV1().
ServiceAccounts(objMeta.Namespace).Get(ctx, objMeta.Name, metav1.GetOptions{})
if sa == nil || apierrors.IsNotFound(err) {
t := true
_, err := r.clientSet.CoreV1().ServiceAccounts(objMeta.Namespace).Create(ctx,
&v1.ServiceAccount{
ObjectMeta: objMeta,
AutomountServiceAccountToken: &t,
}, metav1.CreateOptions{})
if err != nil {
return err
}
}
return err
}
// func (r *OSArtifactReconciler) createCopierRole(ctx context.Context, objMeta metav1.ObjectMeta) error {
// role, err := r.clientSet.RbacV1().
// Roles(objMeta.Namespace).
// Get(ctx, objMeta.Name, metav1.GetOptions{})
// if role == nil || apierrors.IsNotFound(err) {
// _, err := r.clientSet.RbacV1().Roles(objMeta.Namespace).Create(ctx,
// &rbacv1.Role{
// ObjectMeta: objMeta,
// Rules: []rbacv1.PolicyRule{
// // TODO: The actual permissions we need is that to copy to a Pod.
// // The Pod is on another namespace, so we need a cluster wide permission.
// // This can get viral because the controller needs to have the permissions
// // if it is to grant them to the Job.
// {
// Verbs: []string{"list"},
// APIGroups: []string{""},
// Resources: []string{"pods"},
// },
// },
// },
// metav1.CreateOptions{},
// )
// if err != nil {
// return err
// }
// }
// return err
// }
func (r *OSArtifactReconciler) createCopierRoleBinding(ctx context.Context, objMeta metav1.ObjectMeta) error {
newrb := &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: objMeta.Name,
Namespace: r.ArtifactPodInfo.Namespace,
// TODO: We can't have cross-namespace owners. The role binding will have to deleted explicitly by the reconciler (finalizer?)
// OwnerReferences: objMeta.OwnerReferences,
},
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: r.ArtifactPodInfo.Role,
},
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
Name: objMeta.Name,
Namespace: objMeta.Namespace,
},
},
}
rb, err := r.clientSet.RbacV1().
RoleBindings(r.ArtifactPodInfo.Namespace).
Get(ctx, objMeta.Name, metav1.GetOptions{})
if rb == nil || apierrors.IsNotFound(err) {
_, err := r.clientSet.RbacV1().
RoleBindings(r.ArtifactPodInfo.Namespace).
Create(ctx, newrb, metav1.CreateOptions{})
if err != nil {
return err
}
}
return err
}
// createRBAC creates a ServiceAccount, and a binding to the CopierRole so that
// the container that copies the artifacts to the http server Pod has the
// permissions to do so.
func (r *OSArtifactReconciler) createRBAC(ctx context.Context, artifact buildv1alpha1.OSArtifact) error {
objMeta := genObjectMeta(artifact)
err := r.createCopierServiceAccount(ctx, objMeta)
if err != nil {
return errors.Wrap(err, "creating a service account")
}
err = r.createCopierRoleBinding(ctx, objMeta)
if err != nil {
return errors.Wrap(err, "creating a role binding for the copy-role")
}
return err
}
// removeRBAC deletes the role binding between the service account of this artifact
// and the CopierRole. The ServiceAccount is removed automatically through the Owner
// relationship with the OSArtifact. The RoleBinding can't have it as an owner
// because it is in a different Namespace.
func (r *OSArtifactReconciler) removeRBAC(ctx context.Context, artifact buildv1alpha1.OSArtifact) error {
err := r.clientSet.RbacV1().RoleBindings(r.ArtifactPodInfo.Namespace).
Delete(ctx, artifact.Name, metav1.DeleteOptions{})
// Ignore not found. No need to do anything.
if err != nil && apierrors.IsNotFound(err) {
return nil
}
return err
}
func (r *OSArtifactReconciler) removeArtifacts(ctx context.Context, artifact buildv1alpha1.OSArtifact) error {
//Finding Pods using labels
fmt.Printf("r.ArtifactPodInfo = %+v\n", r.ArtifactPodInfo.Label)
pods, err := r.clientSet.CoreV1().Pods(r.ArtifactPodInfo.Namespace).
List(ctx, metav1.ListOptions{LabelSelector: r.ArtifactPodInfo.Label})
if err != nil {
return errors.Wrap(err, fmt.Sprintf("listing pods with label %s in namespace %s", r.ArtifactPodInfo.Label, r.ArtifactPodInfo.Namespace))
}
if len(pods.Items) < 1 {
return errors.New("No artifact pod found")
}
pod := pods.Items[0]
stdout, stderr, err := r.executeRemoteCommand(r.ArtifactPodInfo.Namespace, pod.Name, fmt.Sprintf("rm -rf %s/%s.*", r.ArtifactPodInfo.Path, artifact.Name))
if err != nil {
return errors.Wrap(err, fmt.Sprintf("%s\n%s", stdout, stderr))
}
return nil
}
func (r *OSArtifactReconciler) executeRemoteCommand(namespace, podName, command string) (string, string, error) {
buf := &bytes.Buffer{}
errBuf := &bytes.Buffer{}
request := r.clientSet.CoreV1().RESTClient().
Post().
Namespace(namespace).
Resource("pods").
Name(podName).
SubResource("exec").
VersionedParams(&v1.PodExecOptions{
Command: []string{"/bin/sh", "-c", command},
Stdin: false,
Stdout: true,
Stderr: true,
TTY: true,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(r.restConfig, "POST", request.URL())
if err != nil {
return "", "", err
}
err = exec.Stream(remotecommand.StreamOptions{
Stdout: buf,
Stderr: errBuf,
})
if err != nil {
return "", "", fmt.Errorf("%w Failed executing command %s on %v/%v", err, command, namespace, podName)
}
return buf.String(), errBuf.String(), nil
}

View File

@@ -21,26 +21,46 @@ import (
"fmt"
"time"
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
"github.com/pkg/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/cluster-api/util/patch"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
)
const FinalizerName = "build.kairos.io/osbuilder-finalizer"
type ArtifactPodInfo struct {
Label string
Namespace string
Path string
Role string
}
// OSArtifactReconciler reconciles a OSArtifact object
type OSArtifactReconciler struct {
client.Client
Scheme *runtime.Scheme
clientSet *kubernetes.Clientset
ServingImage, BuildImage string
Scheme *runtime.Scheme
restConfig *rest.Config
clientSet *kubernetes.Clientset
ServingImage, ToolImage, CopierImage string
ArtifactPodInfo ArtifactPodInfo
}
func genObjectMeta(artifact buildv1alpha1.OSArtifact) metav1.ObjectMeta {
return metav1.ObjectMeta{
Name: artifact.Name,
Namespace: artifact.Namespace,
OwnerReferences: genOwner(artifact),
}
}
func genOwner(artifact buildv1alpha1.OSArtifact) []metav1.OwnerReference {
@@ -53,9 +73,15 @@ func genOwner(artifact buildv1alpha1.OSArtifact) []metav1.OwnerReference {
}
}
//+kubebuilder:rbac:groups=build.c3os-x.io,resources=osartifacts,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=build.c3os-x.io,resources=osartifacts/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=build.c3os-x.io,resources=osartifacts/finalizers,verbs=update
//+kubebuilder:rbac:groups=build.kairos.io,resources=osartifacts,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=build.kairos.io,resources=osartifacts/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=build.kairos.io,resources=osartifacts/finalizers,verbs=update
// TODO: Is this ^ how I should have created rbac permissions for the controller?
// - git commit all changes
// - generate code with kubebuilder
// - check if my permissions were removed
// - do it properly
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
@@ -79,17 +105,22 @@ func (r *OSArtifactReconciler) Reconcile(ctx context.Context, req ctrl.Request)
logger.Info(fmt.Sprintf("Reconciling %v", osbuild))
stop, err := r.handleFinalizer(ctx, &osbuild)
if err != nil || stop {
return ctrl.Result{}, err
}
// generate configmap required for building a custom image
desiredConfigMap := r.genConfigMap(osbuild)
logger.Info(fmt.Sprintf("Checking configmap %v", osbuild))
cfgMap, err := r.clientSet.CoreV1().ConfigMaps(req.Namespace).Get(ctx, desiredConfigMap.Name, v1.GetOptions{})
cfgMap, err := r.clientSet.CoreV1().ConfigMaps(req.Namespace).Get(ctx, desiredConfigMap.Name, metav1.GetOptions{})
if cfgMap == nil || apierrors.IsNotFound(err) {
logger.Info(fmt.Sprintf("Creating service %v", desiredConfigMap))
logger.Info(fmt.Sprintf("Creating config map %v", desiredConfigMap))
cfgMap, err = r.clientSet.CoreV1().ConfigMaps(req.Namespace).Create(ctx, desiredConfigMap, v1.CreateOptions{})
_, err = r.clientSet.CoreV1().ConfigMaps(req.Namespace).Create(ctx, desiredConfigMap, metav1.CreateOptions{})
if err != nil {
logger.Error(err, "Failed while creating svc")
logger.Error(err, "Failed while creating config map")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, err
@@ -98,33 +129,21 @@ func (r *OSArtifactReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{Requeue: true}, err
}
desiredService := genService(osbuild)
logger.Info(fmt.Sprintf("Checking service %v", osbuild))
svc, err := r.clientSet.CoreV1().Services(req.Namespace).Get(ctx, desiredService.Name, v1.GetOptions{})
if svc == nil || apierrors.IsNotFound(err) {
logger.Info(fmt.Sprintf("Creating service %v", desiredService))
svc, err = r.clientSet.CoreV1().Services(req.Namespace).Create(ctx, desiredService, v1.CreateOptions{})
if err != nil {
logger.Error(err, "Failed while creating svc")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, err
}
if err != nil {
return ctrl.Result{Requeue: true}, err
}
logger.Info(fmt.Sprintf("Checking deployment %v", osbuild))
desiredDeployment := r.genDeployment(osbuild)
deployment, err := r.clientSet.AppsV1().Deployments(req.Namespace).Get(ctx, desiredDeployment.Name, v1.GetOptions{})
if deployment == nil || apierrors.IsNotFound(err) {
logger.Info(fmt.Sprintf("Creating Deployment %v", deployment))
err = r.createRBAC(ctx, osbuild)
if err != nil {
return ctrl.Result{Requeue: true}, err
}
deployment, err = r.clientSet.AppsV1().Deployments(req.Namespace).Create(ctx, desiredDeployment, v1.CreateOptions{})
desiredJob := r.genJob(osbuild)
job, err := r.clientSet.BatchV1().Jobs(req.Namespace).Get(ctx, desiredJob.Name, metav1.GetOptions{})
if job == nil || apierrors.IsNotFound(err) {
logger.Info(fmt.Sprintf("Creating Job %v", job))
_, err = r.clientSet.BatchV1().Jobs(req.Namespace).Create(ctx, desiredJob, metav1.CreateOptions{})
if err != nil {
logger.Error(err, "Failed while creating deployment")
logger.Error(err, "Failed while creating job")
return ctrl.Result{}, nil
}
@@ -142,7 +161,7 @@ func (r *OSArtifactReconciler) Reconcile(ctx context.Context, req ctrl.Request)
if err != nil {
return ctrl.Result{}, err
}
if deployment.Status.ReadyReplicas == deployment.Status.Replicas {
if job.Status.Succeeded > 0 {
copy.Status.Phase = "Ready"
} else if copy.Status.Phase != "Building" {
copy.Status.Phase = "Building"
@@ -165,13 +184,66 @@ func (r *OSArtifactReconciler) Reconcile(ctx context.Context, req ctrl.Request)
// SetupWithManager sets up the controller with the Manager.
func (r *OSArtifactReconciler) SetupWithManager(mgr ctrl.Manager) error {
clientset, err := kubernetes.NewForConfig(mgr.GetConfig())
cfg := mgr.GetConfig()
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return err
}
r.restConfig = cfg
r.clientSet = clientset
return ctrl.NewControllerManagedBy(mgr).
For(&buildv1alpha1.OSArtifact{}).
Complete(r)
}
// Returns true if reconciliation should stop or false otherwise
func (r *OSArtifactReconciler) handleFinalizer(ctx context.Context, osbuild *buildv1alpha1.OSArtifact) (bool, error) {
// examine DeletionTimestamp to determine if object is under deletion
if osbuild.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !controllerutil.ContainsFinalizer(osbuild, FinalizerName) {
controllerutil.AddFinalizer(osbuild, FinalizerName)
if err := r.Update(ctx, osbuild); err != nil {
return true, err
}
}
} else {
// The object is being deleted
if controllerutil.ContainsFinalizer(osbuild, FinalizerName) {
// our finalizer is present, so lets handle any external dependency
if err := r.finalize(ctx, osbuild); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return true, err
}
// remove our finalizer from the list and update it.
controllerutil.RemoveFinalizer(osbuild, FinalizerName)
if err := r.Update(ctx, osbuild); err != nil {
return true, err
}
}
// Stop reconciliation as the item is being deleted
return true, nil
}
return false, nil
}
// - Remove artifacts from the server Pod
// - Delete role-binding (because it doesn't have the OSArtifact as an owner and won't be deleted automatically)
func (r *OSArtifactReconciler) finalize(ctx context.Context, osbuild *buildv1alpha1.OSArtifact) error {
if err := r.removeRBAC(ctx, *osbuild); err != nil {
return err
}
if err := r.removeArtifacts(ctx, *osbuild); err != nil {
return err
}
return nil
}

View File

@@ -1,39 +0,0 @@
/*
Copyright 2022.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
)
func genService(artifact buildv1alpha1.OSArtifact) *v1.Service {
objMeta := metav1.ObjectMeta{
Name: artifact.Name,
Namespace: artifact.Namespace,
OwnerReferences: genOwner(artifact),
}
return &v1.Service{
ObjectMeta: objMeta,
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{{Name: "http", Port: int32(80), TargetPort: intstr.FromInt(80)}},
Selector: genDeploymentLabel(artifact.Name),
},
}
}

View File

@@ -30,7 +30,7 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
//+kubebuilder:scaffold:imports
)

28
go.mod
View File

@@ -1,11 +1,11 @@
module github.com/c3os-io/osbuilder-operator
module github.com/kairos-io/osbuilder
go 1.18
require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.1.3
github.com/onsi/gomega v1.18.1
github.com/onsi/ginkgo/v2 v2.9.5
github.com/onsi/gomega v1.27.7
github.com/pkg/errors v0.9.1
github.com/rancher-sandbox/ele-testhelpers v0.0.0-20220614101555-2eddf3b113e2
k8s.io/api v0.24.0
@@ -33,24 +33,27 @@ require (
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/go-logr/logr v1.2.0 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/zapr v1.2.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobuffalo/flect v0.2.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.5.7-v3refs // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
github.com/google/uuid v1.1.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.6 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -64,19 +67,20 @@ require (
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
golang.org/x/tools v0.9.1 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.24.0 // indirect
k8s.io/component-base v0.24.0 // indirect
k8s.io/klog/v2 v2.60.1 // indirect

34
go.sum
View File

@@ -82,6 +82,7 @@ github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.m
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -136,6 +137,7 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
@@ -177,6 +179,8 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -189,7 +193,10 @@ github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gobuffalo/flect v0.2.4 h1:BSYA8+T60cdyq+vynaSUjqSVI9mDEg9ZfQUXKmfjo4I=
github.com/gobuffalo/flect v0.2.4/go.mod h1:1ZyCLIbg0YD7sDkzvFdPoOydPtD8y9JQnrOROolUcM8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -233,6 +240,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -255,6 +264,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
@@ -277,6 +288,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
@@ -373,6 +385,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -404,12 +417,18 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -649,6 +668,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -746,9 +767,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -759,6 +784,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -826,7 +853,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717 h1:hI3jKY4Hpf63ns040onEbB3dAkR/H/P83hw1TG8dD3Y=
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -964,6 +994,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -994,6 +1026,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

32
main.go
View File

@@ -31,8 +31,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
buildv1alpha1 "github.com/c3os-io/osbuilder-operator/api/v1alpha1"
"github.com/c3os-io/osbuilder-operator/controllers"
buildv1alpha1 "github.com/kairos-io/osbuilder/api/v1alpha1"
"github.com/kairos-io/osbuilder/controllers"
//+kubebuilder:scaffold:imports
)
@@ -52,10 +52,21 @@ func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
var buildImage, serveImage string
var serveImage, toolImage, copierImage string
var copyToPodLabel, copyToNamespace, copyToPath, copierRole string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&buildImage, "build-image", "quay.io/costoolkit/elemental-cli:v0.0.15-8a78e6b", "Build image.")
flag.StringVar(&copierImage, "copier-image", "quay.io/kairos/kubectl", "The image that is used to copy artifacts to the server pod.")
flag.StringVar(&serveImage, "serve-image", "nginx", "Serve image.")
// It needs luet inside
flag.StringVar(&toolImage, "tool-image", "quay.io/kairos/osbuilder-tools:latest", "Tool image.")
// Information on where to copy the artifacts
flag.StringVar(&copyToPodLabel, "copy-to-pod-label", "", "The label of the Pod to which artifacts should be copied.")
flag.StringVar(&copyToNamespace, "copy-to-namespace", "", "The namespace of the copy-to-pod-label Pod.")
flag.StringVar(&copyToPath, "copy-to-path", "", "The path under which to copy artifacts in the copy-to-pod-label Pod.")
flag.StringVar(&copierRole, "copy-role", "", "The name or the Kubernetes Role that has the permissions to copy artifacts to the copy-to-pod-label Pod")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
@@ -75,7 +86,7 @@ func main() {
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "98ca89ca.c3os-x.io",
LeaderElectionID: "98ca89ca.kairos.io",
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
// when the Manager ends. This requires the binary to immediately end when the
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
@@ -96,8 +107,15 @@ func main() {
if err = (&controllers.OSArtifactReconciler{
Client: mgr.GetClient(),
ServingImage: serveImage,
BuildImage: buildImage,
Scheme: mgr.GetScheme(),
ToolImage: toolImage,
CopierImage: copierImage,
ArtifactPodInfo: controllers.ArtifactPodInfo{
Label: copyToPodLabel,
Namespace: copyToNamespace,
Path: copyToPath,
Role: copierRole,
},
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "OSArtifact")
os.Exit(1)

6
renovate.json Normal file
View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}

View File

@@ -1,7 +1,7 @@
#!/bin/bash
KUBE_VERSION=${KUBE_VERSION:-v1.22.7}
CLUSTER_NAME="${CLUSTER_NAME:-c3os-osbuilder-e2e}"
CLUSTER_NAME="${CLUSTER_NAME:-kairos-osbuilder-e2e}"
if ! kind get clusters | grep "$CLUSTER_NAME"; then
cat << EOF > kind.config
@@ -29,4 +29,4 @@ kubectl wait --for=condition=Ready node/$CLUSTER_NAME-control-plane
export EXTERNAL_IP=$(kubectl get nodes -o jsonpath='{.items[].status.addresses[?(@.type == "InternalIP")].address}')
export BRIDGE_IP="172.18.0.1"
kubectl get nodes -o wide
cd $ROOT_DIR/tests && ginkgo -r -v ./e2e
cd $ROOT_DIR/tests && $GINKGO -r -v ./e2e

View File

@@ -0,0 +1,114 @@
package e2e_test
import (
"bytes"
"fmt"
"os/exec"
"strings"
"time"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
kubectl "github.com/rancher-sandbox/ele-testhelpers/kubectl"
)
var _ = Describe("ISO build test", func() {
//k := kubectl.New()
Context("registration", func() {
AfterEach(func() {
kubectl.New().Delete("osartifacts", "-n", "default", "hello-kairos")
})
It("creates a simple iso", func() {
err := kubectl.Apply("", "../../tests/fixtures/simple.yaml")
Expect(err).ToNot(HaveOccurred())
itHasTheCorrectImage()
itHasTheCorrectLabels()
itCopiesTheArtifacts()
By("deleting the custom resource", func() {
err = kubectl.New().Delete("osartifacts", "-n", "default", "hello-kairos")
Expect(err).ToNot(HaveOccurred())
})
itCleansUpRoleBindings()
itDeletesTheArtifacts()
})
})
})
func itHasTheCorrectImage() {
Eventually(func() string {
b, _ := kubectl.GetData("default", "osartifacts", "hello-kairos", "jsonpath={.spec.imageName}")
fmt.Printf("looking for image core-opensuse:latest = %+v\n", string(b))
return string(b)
}, 2*time.Minute, 2*time.Second).Should(Equal("quay.io/kairos/core-opensuse:latest"))
}
func itHasTheCorrectLabels() {
Eventually(func() string {
b, _ := kubectl.GetData("default", "jobs", "hello-kairos", "jsonpath={.spec.template.metadata.labels.osbuild}")
fmt.Printf("looking for label workloadhello-kairos = %+v\n", string(b))
return string(b)
}, 2*time.Minute, 2*time.Second).Should(Equal("workloadhello-kairos"))
}
func itCopiesTheArtifacts() {
nginxNamespace := "osartifactbuilder-operator-system"
Eventually(func() string {
podName := strings.TrimSpace(findPodsWithLabel(nginxNamespace, "app.kubernetes.io/name=osbuilder-nginx"))
out, _ := kubectl.RunCommandWithOutput(nginxNamespace, podName, "ls /usr/share/nginx/html")
return out
}, 15*time.Minute, 2*time.Second).Should(MatchRegexp("hello-kairos.iso"))
}
func itCleansUpRoleBindings() {
nginxNamespace := "osartifactbuilder-operator-system"
Eventually(func() string {
rb := findRoleBindings(nginxNamespace)
return rb
}, 3*time.Minute, 2*time.Second).ShouldNot(MatchRegexp("hello-kairos"))
}
func itDeletesTheArtifacts() {
nginxNamespace := "osartifactbuilder-operator-system"
Eventually(func() string {
podName := findPodsWithLabel(nginxNamespace, "app.kubernetes.io/name=osbuilder-nginx")
out, err := kubectl.RunCommandWithOutput(nginxNamespace, podName, "ls /usr/share/nginx/html")
Expect(err).ToNot(HaveOccurred(), out)
return out
}, 3*time.Minute, 2*time.Second).ShouldNot(MatchRegexp("hello-kairos.iso"))
}
func findPodsWithLabel(namespace, label string) string {
kubectlCommand := fmt.Sprintf("kubectl get pods -n %s -l %s --no-headers -o custom-columns=\":metadata.name\" | head -n1", namespace, label)
cmd := exec.Command("bash", "-c", kubectlCommand)
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
Expect(err).ToNot(HaveOccurred(), stderr.String())
return strings.TrimSpace(out.String())
}
func findRoleBindings(namespace string) string {
kubectlCommand := fmt.Sprintf("kubectl get rolebindings -n %s --no-headers -o custom-columns=\":metadata.name\"", namespace)
cmd := exec.Command("bash", "-c", kubectlCommand)
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
err := cmd.Run()
Expect(err).ToNot(HaveOccurred(), stderr.String())
return strings.TrimSpace(out.String())
}

View File

@@ -1,39 +1,13 @@
package e2e_test
import (
"time"
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
kubectl "github.com/rancher-sandbox/ele-testhelpers/kubectl"
)
var _ = Describe("ISO build test", func() {
//k := kubectl.New()
Context("registration", func() {
AfterEach(func() {
kubectl.New().Delete("osartifacts", "-n", "default", "hello-c3os")
})
It("creates a simple iso", func() {
err := kubectl.Apply("", "../../tests/fixtures/simple.yaml")
Expect(err).ToNot(HaveOccurred())
Eventually(func() string {
b, _ := kubectl.GetData("default", "osartifacts", "hello-c3os", "jsonpath={.spec.imageName}")
return string(b)
}, 2*time.Minute, 2*time.Second).Should(Equal("quay.io/c3os/c3os:opensuse-latest"))
Eventually(func() string {
b, _ := kubectl.GetData("default", "deployments", "hello-c3os", "jsonpath={.spec.template.metadata.labels.osbuild}")
return string(b)
}, 2*time.Minute, 2*time.Second).Should(Equal("workloadhello-c3os"))
Eventually(func() string {
b, _ := kubectl.GetData("default", "deployments", "hello-c3os", "jsonpath={.spec.status.unavailableReplicas}")
return string(b)
}, 15*time.Minute, 2*time.Second).ShouldNot(Equal("1"))
})
})
})
func TestE2e(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "kairos-operator e2e test Suite")
}

View File

@@ -1,13 +0,0 @@
package e2e_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestE2e(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "c3os-operator e2e test Suite")
}

View File

@@ -1,7 +1,52 @@
apiVersion: build.c3os-x.io/v1alpha1
apiVersion: build.kairos.io/v1alpha1
kind: OSArtifact
metadata:
name: hello-c3os
name: hello-kairos
spec:
imageName: "quay.io/c3os/c3os:opensuse-latest"
iso: true
imageName: "quay.io/kairos/core-opensuse:latest"
iso: true
bundles:
- quay.io/kairos/packages:goreleaser-utils-1.13.1
grubConfig: |
search --file --set=root /boot/kernel.xz
set default=0
set timeout=10
set timeout_style=menu
set linux=linux
set initrd=initrd
if [ "${grub_cpu}" = "x86_64" -o "${grub_cpu}" = "i386" -o "${grub_cpu}" = "arm64" ];then
if [ "${grub_platform}" = "efi" ]; then
if [ "${grub_cpu}" != "arm64" ]; then
set linux=linuxefi
set initrd=initrdefi
fi
fi
fi
if [ "${grub_platform}" = "efi" ]; then
echo "Please press 't' to show the boot menu on this console"
fi
set font=($root)/boot/${grub_cpu}/loader/grub2/fonts/unicode.pf2
if [ -f ${font} ];then
loadfont ${font}
fi
menuentry "install" --class os --unrestricted {
echo Loading kernel...
$linux ($root)/boot/kernel.xz cdroot root=live:CDLABEL=COS_LIVE rd.live.dir=/ rd.live.squashimg=rootfs.squashfs console=tty1 console=ttyS0 rd.cos.disable vga=795 nomodeset nodepair.enable
echo Loading initrd...
$initrd ($root)/boot/rootfs.xz
}
if [ "${grub_platform}" = "efi" ]; then
hiddenentry "Text mode" --hotkey "t" {
set textmode=true
terminal_output console
}
fi
cloudConfig: |
#node-config
install:
device: "/dev/sda"
reboot: true
poweroff: true
auto: true # Required, for automated installations

103
tools-image/Dockerfile Normal file
View File

@@ -0,0 +1,103 @@
# https://quay.io/repository/kairos/packages?tab=tags&tag=latest
ARG LEAP_VERSION=15.4
ARG LUET_VERSION=0.33.0
FROM quay.io/luet/base:$LUET_VERSION AS luet
FROM opensuse/leap:$LEAP_VERSION as luet-install
COPY --from=luet /usr/bin/luet /usr/bin/luet
ENV LUET_NOLOCK=true
ENV TMPDIR=/tmp
COPY luet.yaml /etc/luet/luet.yaml
RUN luet install -y system/elemental-cli
RUN luet install -y livecd/grub2 --system-target /grub2
RUN luet install -y livecd/grub2-efi-image --system-target /efi
# remove luet tmp files. Side effect of setting the system-target is that it treats it as a root fs
RUN rm -Rf /grub2/var
RUN rm -Rf /efi/var
## amd64 Live CD artifacts
FROM quay.io/kairos/packages:grub2-livecd-0.0.6 AS grub2
FROM quay.io/kairos/packages:grub2-efi-image-livecd-0.0.6 AS efi
## RPI64
## Firmware is in the amd64 repo (noarch)
FROM quay.io/kairos/packages:u-boot-rpi64-firmware-2021.01-5.1 AS rpi-u-boot
FROM quay.io/kairos/packages:raspberrypi-firmware-firmware-2021.03.10-2.1 AS rpi-firmware
FROM quay.io/kairos/packages:raspberrypi-firmware-config-firmware-2021.03.10-2.1 AS rpi-firmware-config
FROM quay.io/kairos/packages:raspberrypi-firmware-dt-firmware-2021.03.15-2.1 AS rpi-firmware-dt
## PineBook64 Pro
FROM quay.io/kairos/packages:u-boot-rockchip-arm-vendor-blob-0.1 AS pinebook-u-boot
## Generic ARM artifacts
FROM quay.io/kairos/packages-arm64:grub-efi-static-0.1 AS grub-efi
FROM quay.io/kairos/packages-arm64:grub-config-static-0.1 AS grub-config
FROM quay.io/kairos/packages-arm64:grub-artifacts-static-0.1 AS grub-artifacts
## RAW images
FROM quay.io/kairos/packages:grub-efi-static-0.1 AS grub-raw-efi
FROM quay.io/kairos/packages:grub-config-static-0.1 AS grub-raw-config
FROM quay.io/kairos/packages:grub-artifacts-static-0.1 AS grub-raw-artifacts
FROM opensuse/leap:$LEAP_VERSION
COPY --from=luet-install /usr/bin/elemental /usr/bin/elemental
COPY --from=luet /usr/bin/luet /usr/bin/luet
# ISO files
COPY --from=luet-install /grub2 /grub2
COPY --from=luet-install /efi /efi
# RAW images
COPY --from=grub-raw-efi / /raw/grub
COPY --from=grub-raw-config / /raw/grubconfig
COPY --from=grub-raw-artifacts / /raw/grubartifacts
# RPI64
COPY --from=rpi-u-boot / /rpi/u-boot
COPY --from=rpi-firmware / /rpi/rpi-firmware
COPY --from=rpi-firmware-config / /rpi/rpi-firmware-config
COPY --from=rpi-firmware-dt / /rpi/rpi-firmware-dt
# Pinebook
COPY --from=pinebook-u-boot / /pinebookpro/u-boot
# Generic
COPY --from=grub-efi / /arm/grub/efi
COPY --from=grub-config / /arm/grub/config
COPY --from=grub-artifacts / /arm/grub/artifacts
RUN zypper ref && zypper dup -y
## ISO Build depedencies
RUN zypper ref && zypper in -y xfsprogs parted util-linux-systemd e2fsprogs curl util-linux udev rsync grub2 dosfstools grub2-x86_64-efi squashfs mtools xorriso lvm2
RUN mkdir /config
# Arm image build deps
RUN zypper in -y jq docker git curl gptfdisk kpartx sudo
# Netboot
RUN zypper in -y cdrtools
# cloud images
RUN zypper in -y bc qemu-tools
# ISO build config
COPY ./config.yaml /config/manifest.yaml
COPY ./entrypoint.sh /entrypoint.sh
COPY ./add-cloud-init.sh /add-cloud-init.sh
# ARM helpers
COPY ./build-arm-image.sh /build-arm-image.sh
COPY ./arm /arm
COPY ./prepare_arm_images.sh /prepare_arm_images.sh
# RAW images helpers
COPY ./gce.sh /gce.sh
COPY ./raw-images.sh /raw-images.sh
COPY ./azure.sh /azure.sh
COPY ./netboot.sh /netboot.sh
COPY defaults.yaml /defaults.yaml
ENTRYPOINT [ "/entrypoint.sh" ]

18
tools-image/add-cloud-init.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
# docker run --entrypoint /add-cloud-init.sh -v $PWD:/work -ti --rm test https://github.com/kairos-io/kairos/releases/download/v1.1.2/kairos-alpine-v1.1.2.iso /work/test.iso /work/config.yaml
set -ex
ISO=$1
OUT=$2
CONFIG=$3
case ${ISO} in
http*)
curl -L "${ISO}" -o in.iso
ISO=in.iso
;;
esac
# Needs xorriso >=1.5.4
xorriso -indev $ISO -outdev $OUT -map $CONFIG /config.yaml -boot_image any replay

View File

@@ -0,0 +1,21 @@
#!/bin/bash
image=$1
if [ -z "$image" ]; then
echo "No image specified"
exit 1
fi
if [ ! -e "$WORKDIR/luet.yaml" ]; then
ls -liah $WORKDIR
echo "No valid config file"
cat "$WORKDIR/luet.yaml"
exit 1
fi
sudo luet install --config $WORKDIR/luet.yaml -y --system-target $WORKDIR firmware/odroid-c2
# conv=notrunc ?
dd if=$WORKDIR/bl1.bin.hardkernel of=$image conv=fsync bs=1 count=442
dd if=$WORKDIR/bl1.bin.hardkernel of=$image conv=fsync bs=512 skip=1 seek=1
dd if=$WORKDIR/u-boot.odroidc2 of=$image conv=fsync bs=512 seek=97

View File

@@ -0,0 +1,19 @@
#!/bin/bash
image=$1
if [ -z "$image" ]; then
echo "No image specified"
exit 1
fi
LOADER_OFFSET=${LOADER_OFFSET:-"64"}
LOADER_IMAGE=${LOADER_IMAGE:-"idbloader.img"}
UBOOT_IMAGE=${UBOOT_IMAGE:-"u-boot.itb"}
UBOOT_OFFSET=${UBOOT_OFFSET:-"16384"}
echo "Writing idbloader"
dd conv=notrunc if=/pinebookpro/u-boot/usr/lib/u-boot/pinebook-pro-rk3399/${LOADER_IMAGE} of="$image" conv=fsync seek=${LOADER_OFFSET}
echo "Writing u-boot image"
dd conv=notrunc if=/pinebookpro/u-boot/usr/lib/u-boot/pinebook-pro-rk3399/${UBOOT_IMAGE} of="$image" conv=fsync seek=${UBOOT_OFFSET}
sync $image

24
tools-image/arm/boards/rpi64.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
partprobe
kpartx -va $DRIVE
image=$1
if [ -z "$image" ]; then
echo "No image specified"
exit 1
fi
set -ax
TEMPDIR="$(mktemp -d)"
echo $TEMPDIR
mount "${device}p1" "${TEMPDIR}"
for dir in /rpi/u-boot /rpi/rpi-firmware /rpi/rpi-firmware-config /rpi/rpi-firmware-dt
do
cp -rfv ${dir}/* $TEMPDIR
done
umount "${TEMPDIR}"

17
tools-image/azure.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Transform a raw image disk to azure vhd
RAWIMAGE="$1"
VHDDISK="${2:-disk.vhd}"
cp -rf $RAWIMAGE $VHDDISK.work
MB=$((1024*1024))
size=$(qemu-img info -f raw --output json "$RAWIMAGE" | gawk 'match($0, /"virtual-size": ([0-9]+),/, val) {print val[1]}')
# shellcheck disable=SC2004
ROUNDED_SIZE=$(((($size+$MB-1)/$MB)*$MB))
echo "Resizing raw image to $ROUNDED_SIZE"
qemu-img resize -f raw "$VHDDISK.work" $ROUNDED_SIZE
echo "Converting $RAWIMAGE to $VHDDISK"
qemu-img convert -f raw -o subformat=fixed,force_size -O vpc "$VHDDISK.work" "$VHDDISK"
echo "Done"
rm -rf "$VHDDISK.work"

465
tools-image/build-arm-image.sh Executable file
View File

@@ -0,0 +1,465 @@
#!/bin/bash
## This is a re-adaptation of https://github.com/rancher/elemental-toolkit/blob/main/images/arm-img-builder.sh
set -ex
load_vars() {
model=${MODEL:-odroid_c2}
disable_lvm=${DISABLE_LVM:-false}
directory=${DIRECTORY:-}
output_image="${OUTPUT_IMAGE:-arm.img}"
# Img creation options. Size is in MB for all of the vars below
size="${SIZE:-7608}"
state_size="${STATE_SIZE:-4992}"
oem_size="${OEM_SIZE:-64}"
recovery_size="${RECOVERY_SIZE:-2192}"
default_active_size="${DEFAULT_ACTIVE_SIZE:-2400}"
## Repositories
final_repo="${FINAL_REPO:-quay.io/costoolkit/releases-teal-arm64}"
repo_type="${REPO_TYPE:-docker}"
# Warning: these default values must be aligned with the values provided
# in 'packages/cos-config/cos-config', provide an environment file using the
# --cos-config flag if different values are needed.
: "${OEM_LABEL:=COS_OEM}"
: "${RECOVERY_LABEL:=COS_RECOVERY}"
: "${ACTIVE_LABEL:=COS_ACTIVE}"
: "${PASSIVE_LABEL:=COS_PASSIVE}"
: "${PERSISTENT_LABEL:=COS_PERSISTENT}"
: "${SYSTEM_LABEL:=COS_SYSTEM}"
: "${STATE_LABEL:=COS_STATE}"
}
cleanup() {
sync
sync
sleep 5
sync
if [ -n "$EFI" ]; then
rm -rf $EFI
fi
if [ -n "$RECOVERY" ]; then
rm -rf $RECOVERY
fi
if [ -n "$STATEDIR" ]; then
rm -rf $STATEDIR
fi
if [ -n "$TARGET" ]; then
umount $TARGET || true
umount $LOOP || true
rm -rf $TARGET
fi
if [ -n "$WORKDIR" ]; then
rm -rf $WORKDIR
fi
if [ -n "$DRIVE" ]; then
umount $DRIVE || true
fi
if [ -n "$recovery" ]; then
umount $recovery || true
fi
if [ -n "$state" ]; then
umount $state || true
fi
if [ -n "$efi" ]; then
umount $efi || true
fi
if [ -n "$oem" ]; then
umount $oem || true
fi
losetup -D || true
}
ensure_dir_structure() {
local target=$1
for mnt in /sys /proc /dev /tmp /boot /usr/local /oem
do
if [ ! -d "${target}${mnt}" ]; then
mkdir -p ${target}${mnt}
fi
done
}
usage()
{
echo "Usage: $0 [options] image.img"
echo ""
echo "Example: $0 --cos-config cos-config --model odroid-c2 --docker-image <image> output.img"
echo ""
echo "Flags:"
echo " --cos-config: (optional) Specifies a cos-config file for required environment variables"
echo " --config: (optional) Specify a cloud-init config file to embed into the final image"
echo " --manifest: (optional) Specify a manifest file to customize efi/grub packages installed into the image"
echo " --size: (optional) Image size (MB)"
echo " --state-partition-size: (optional) Size of the state partition (MB)"
echo " --recovery-partition-size: (optional) Size of the recovery partition (MB)"
echo " --images-size: (optional) Size of the active/passive/recovery images (MB)"
echo " --docker-image: (optional) A container image which will be used for active/passive/recovery system"
echo " --local: (optional) Use local repository when building"
echo " --directory: (optional) A directory which will be used for active/passive/recovery system"
echo " --model: (optional) The board model"
echo " --efi-dir: (optional) A directory with files which will be added to the efi partition"
echo " --disable-lvm: (optional- no arguments) LVM for the recovery and oem partitions will be disabled"
echo " --use-lvm: (deprecated and ignored. Kept for backwards compatibility)"
exit 1
}
get_url()
{
FROM=$1
TO=$2
case $FROM in
ftp*|http*|tftp*)
n=0
attempts=5
until [ "$n" -ge "$attempts" ]
do
curl -o $TO -fL ${FROM} && break
n=$((n+1))
echo "Failed to download, retry attempt ${n} out of ${attempts}"
sleep 2
done
;;
*)
cp -f $FROM $TO
;;
esac
}
trap "cleanup" 1 2 3 6 9 14 15 EXIT
load_vars
while [ "$#" -gt 0 ]; do
case $1 in
--cos-config)
shift 1
cos_config=$1
;;
--config)
shift 1
config=$1
;;
--manifest)
shift 1
manifest=$1
;;
--size)
shift 1
size=$1
;;
--local)
local_build=true
;;
--state-partition-size)
shift 1
state_size=$1
;;
--recovery-partition-size)
shift 1
recovery_size=$1
;;
--images-size)
shift 1
default_active_size=$1
;;
--docker-image)
shift 1
CONTAINER_IMAGE=$1
;;
--directory)
shift 1
directory=$1
;;
--model)
shift 1
model=$1
;;
--efi-dir)
shift 1
efi_dir=$1
;;
--final-repo)
shift 1
final_repo=$1
;;
--repo-type)
shift 1
repo_type=$1
;;
--disable-lvm)
disable_lvm=true
;;
--use-lvm)
disable_lvm=false
;;
-h)
usage
;;
--help)
usage
;;
*)
if [ "$#" -gt 2 ]; then
usage
fi
output_image=$1
break
;;
esac
shift 1
done
if [ "$model" == "rpi64" ]; then
container_image=${CONTAINER_IMAGE:-quay.io/costoolkit/examples:rpi-latest}
else
# Odroid C2 image contains kernel-default-extra, might have broader support
container_image=${CONTAINER_IMAGE:-quay.io/costoolkit/examples:odroid-c2-latest}
fi
if [ -n "$cos_config" ] && [ -e "$cos_config" ]; then
source "$cos_config"
fi
if [ -z "$output_image" ]; then
echo "No image file specified"
exit 1
fi
if [ -n "$manifest" ]; then
YQ_PACKAGES_COMMAND=(yq e -o=json "$manifest")
final_repo=${final_repo:-$(yq e ".raw_disk.$model.repo" "${manifest}")}
fi
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
echo "Image Size: $size MB."
echo "Recovery Partition: $recovery_size."
echo "State Partition: $state_size MB."
echo "Images size (active/passive/recovery.img): $default_active_size MB."
echo "Model: $model"
if [ -n "$container_image" ] && [ -z "$directory" ]; then
echo "Container image: $container_image"
fi
if [ -n "$directory" ]; then
echo "Root from directory: $directory"
fi
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
# Temp dir used during build
WORKDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
#ROOT_DIR=$(git rev-parse --show-toplevel)
TARGET=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
STATEDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
export WORKDIR
# Prepare active.img
echo ">> Preparing active.img"
mkdir -p ${STATEDIR}/cOS
dd if=/dev/zero of=${STATEDIR}/cOS/active.img bs=1M count=$default_active_size
mkfs.ext2 ${STATEDIR}/cOS/active.img -L ${ACTIVE_LABEL}
sync
LOOP=$(losetup --show -f ${STATEDIR}/cOS/active.img)
if [ -z "$LOOP" ]; then
echo "No device"
exit 1
fi
mount -t ext2 $LOOP $TARGET
ensure_dir_structure $TARGET
# Download the container image
if [ -z "$directory" ]; then
echo ">>> Downloading container image"
elemental pull-image $( (( $local_build == 'true')) && printf %s '--local' ) $container_image $TARGET
else
echo ">>> Copying files from $directory"
rsync -axq --exclude='host' --exclude='mnt' --exclude='proc' --exclude='sys' --exclude='dev' --exclude='tmp' ${directory}/ $TARGET
fi
umount $TARGET
sync
if [ -n "$LOOP" ]; then
losetup -d $LOOP
fi
echo ">> Preparing passive.img"
cp -rfv ${STATEDIR}/cOS/active.img ${STATEDIR}/cOS/passive.img
tune2fs -L ${PASSIVE_LABEL} ${STATEDIR}/cOS/passive.img
# Preparing recovery
echo ">> Preparing recovery.img"
RECOVERY=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
if [ -z "$RECOVERY" ]; then
echo "No recovery directory"
exit 1
fi
mkdir -p ${RECOVERY}/cOS
cp -rfv ${STATEDIR}/cOS/active.img ${RECOVERY}/cOS/recovery.img
tune2fs -L ${SYSTEM_LABEL} ${RECOVERY}/cOS/recovery.img
# Install real grub config to recovery
cp -rfv /arm/grub/config/* $RECOVERY
mkdir -p $RECOVERY/grub2
cp -rfv /arm/grub/artifacts/* $RECOVERY/grub2
sync
# Prepare efi files
echo ">> Preparing EFI partition"
EFI=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
if [ -z "$EFI" ]; then
echo "No EFI directory"
exit 1
fi
cp -rfv /arm/grub/efi/* $EFI
if [ -n "$EFI" ] && [ -n "$efi_dir" ]; then
echo "Copy $efi_dir to EFI directory"
cp -rfv $efi_dir/* $EFI
fi
echo ">> Writing image and partition table"
dd if=/dev/zero of="${output_image}" bs=1024000 count="${size}" || exit 1
if [ "$model" == "rpi64" ]; then
sgdisk -n 1:8192:+96M -c 1:EFI -t 1:0c00 ${output_image}
else
sgdisk -n 1:8192:+16M -c 1:EFI -t 1:0700 ${output_image}
fi
sgdisk -n 2:0:+${state_size}M -c 2:state -t 2:8300 ${output_image}
if [ "$disable_lvm" == 'true' ]; then
sgdisk -n 3:0:+${recovery_size}M -c 3:recovery -t 3:8300 ${output_image}
else
sgdisk -n 3:0:+$(( ${recovery_size} + ${oem_size} ))M -c 3:lvm -t 3:8e00 ${output_image}
fi
sgdisk -n 4:0:+64M -c 4:persistent -t 4:8300 ${output_image}
sgdisk -m 1:2:3:4 ${output_image}
if [ "$model" == "rpi64" ]; then
sfdisk --part-type ${output_image} 1 c
fi
# Prepare the image and copy over the files
export DRIVE=$(losetup -f "${output_image}" --show)
if [ -z "${DRIVE}" ]; then
echo "Cannot execute losetup for $output_image"
exit 1
fi
device=${DRIVE/\/dev\//}
if [ -z "$device" ]; then
echo "No device"
exit 1
fi
export device="/dev/mapper/${device}"
partprobe
kpartx -va $DRIVE
echo ">> Populating partitions"
efi=${device}p1
state=${device}p2
recovery=${device}p3
persistent=${device}p4
oem_lv=/dev/mapper/KairosVG-oem
recovery_lv=/dev/mapper/KairosVG-recovery
# Create partitions (RECOVERY, STATE, COS_PERSISTENT)
mkfs.vfat -F 32 ${efi}
fatlabel ${efi} COS_GRUB
if [ "$disable_lvm" == 'true' ]; then
mkfs.ext4 -F -L ${RECOVERY_LABEL} $recovery
else
pvcreate $recovery
vgcreate KairosVG $recovery
lvcreate -Z n -n oem -L ${oem_size} KairosVG
lvcreate -Z n -n recovery -l 100%FREE KairosVG
vgchange -ay
vgmknodes
mkfs.ext4 -F -L ${OEM_LABEL} $oem_lv
mkfs.ext4 -F -L ${RECOVERY_LABEL} $recovery_lv
fi
mkfs.ext4 -F -L ${STATE_LABEL} $state
mkfs.ext4 -F -L ${PERSISTENT_LABEL} $persistent
mkdir $WORKDIR/state
mkdir $WORKDIR/recovery
mkdir $WORKDIR/efi
if [ "$disable_lvm" == 'true' ]; then
mount $recovery $WORKDIR/recovery
else
mount $recovery_lv $WORKDIR/recovery
fi
mount $state $WORKDIR/state
mount $efi $WORKDIR/efi
mkdir $WORKDIR/persistent
mount $persistent $WORKDIR/persistent
mkdir -p $WORKDIR/persistent/cloud-config
cp -rfv /defaults.yaml $WORKDIR/persistent/cloud-config/01_defaults.yaml
grub2-editenv $WORKDIR/state/grub_oem_env set "default_menu_entry=Kairos"
# Set a OEM config file if specified
if [ -n "$config" ]; then
echo ">> Copying $config OEM config file"
get_url $config $WORKDIR/persistent/cloud-config/99_custom.yaml
fi
umount $WORKDIR/persistent
# Copy over content
cp -arf $EFI/* $WORKDIR/efi
cp -arf $RECOVERY/* $WORKDIR/recovery
cp -arf $STATEDIR/* $WORKDIR/state
umount $WORKDIR/recovery
umount $WORKDIR/state
umount $WORKDIR/efi
if [ "$disable_lvm" == 'false' ]; then
vgchange -an
fi
sync
# Flash uboot and vendor-specific bits
echo ">> Performing $model specific bits.."
/arm/boards/$model.sh ${DRIVE}
dmsetup remove $DRIVE
sync
sleep 5
sync
kpartx -dv $DRIVE || true
umount $DRIVE || true
echo ">> Done writing $output_image"
echo ">> Creating SHA256 sum"
sha256sum $output_image > $output_image.sha256
cleanup

6
tools-image/config.yaml Normal file
View File

@@ -0,0 +1,6 @@
iso:
uefi:
- dir:/efi
image:
- dir:/efi
- dir:/grub2

View File

@@ -0,0 +1,8 @@
#cloud-config
name: "Default user"
stages:
initramfs:
- name: "Set default user/pass"
users:
kairos:
passwd: "kairos"

5
tools-image/entrypoint.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -ex
elemental --config-dir /config $@

15
tools-image/gce.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/bin/bash
# Transform a raw image disk to gce compatible
RAWIMAGE="$1"
OUT="${2:-$RAWIMAGE.gce.raw}"
cp -rf $RAWIMAGE $OUT
GB=$((1024*1024*1024))
size=$(qemu-img info -f raw --output json "$OUT" | gawk 'match($0, /"virtual-size": ([0-9]+),/, val) {print val[1]}')
# shellcheck disable=SC2004
ROUNDED_SIZE=$(echo "$size/$GB+1"|bc)
echo "Resizing raw image from \"$size\"MB to \"$ROUNDED_SIZE\"GB"
qemu-img resize -f raw "$OUT" "$ROUNDED_SIZE"G
echo "Compressing raw image $OUT to $OUT.tar.gz"
tar -c -z --format=oldgnu -f "$OUT".tar.gz $OUT

24
tools-image/luet.yaml Normal file
View File

@@ -0,0 +1,24 @@
general:
debug: false
spinner_charset: 9
logging:
enable_emoji: false
repositories:
- name: "kairos"
description: "kairos repository"
type: "docker"
arch: amd64
cached: true
priority: 2
urls:
- "quay.io/kairos/packages"
reference: 20230512113938-repository.yaml
- name: "kairos-arm64"
description: "kairos repository arm64"
type: "docker"
arch: arm64
cached: true
priority: 2
urls:
- "quay.io/kairos/packages-arm64"
reference: 20230512115044-repository.yaml

25
tools-image/netboot.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
# Extracts squashfs, kernel, initrd and generates a ipxe template script
ISO=$1
OUTPUT_NAME=$2
ARTIFACT_NAME=$(basename $OUTPUT_NAME)
isoinfo -x /rootfs.squashfs -R -i $ISO > $OUTPUT_NAME.squashfs
isoinfo -x /boot/kernel -R -i $ISO > $OUTPUT_NAME-kernel
isoinfo -x /boot/initrd -R -i $ISO > $OUTPUT_NAME-initrd
URL=${URL:-https://github.com/kairos-io/kairos/releases/download}
cat > $OUTPUT_NAME.ipxe << EOF
#!ipxe
set url ${URL}/
set kernel $ARTIFACT_NAME-kernel
set initrd $ARTIFACT_NAME-initrd
set rootfs $ARTIFACT_NAME.squashfs
# set config https://example.com/machine-config
# set cmdline extra.values=1
kernel \${url}/\${kernel} initrd=\${initrd} ip=dhcp rd.cos.disable root=live:\${url}/\${rootfs} netboot nodepair.enable config_url=\${config} console=tty1 console=ttyS0 \${cmdline}
initrd \${url}/\${initrd}
boot
EOF

128
tools-image/prepare_arm_images.sh Executable file
View File

@@ -0,0 +1,128 @@
#!/bin/bash
# This script prepares Kairos state, recovery, oem and pesistent partitions as img files.
set -e
# Temp dir used during build
WORKDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
TARGET=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
STATEDIR=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
: "${OEM_LABEL:=COS_OEM}"
: "${RECOVERY_LABEL:=COS_RECOVERY}"
: "${ACTIVE_LABEL:=COS_ACTIVE}"
: "${PASSIVE_LABEL:=COS_PASSIVE}"
: "${PERSISTENT_LABEL:=COS_PERSISTENT}"
: "${SYSTEM_LABEL:=COS_SYSTEM}"
: "${STATE_LABEL:=COS_STATE}"
size="${SIZE:-7544}"
state_size="${STATE_SIZE:-4992}"
recovery_size="${RECOVERY_SIZE:-2192}"
default_active_size="${DEFAULT_ACTIVE_SIZE:-2400}"
container_image="${container_image:-quay.io/kairos/kairos-opensuse-leap-arm-rpi:v1.5.1-k3sv1.25.6-k3s1}"
ensure_dir_structure() {
local target=$1
for mnt in /sys /proc /dev /tmp /boot /usr/local /oem
do
if [ ! -d "${target}${mnt}" ]; then
mkdir -p ${target}${mnt}
fi
done
}
mkdir -p $WORKDIR/tmpefi
# Create the EFI partition FAT16 and include the EFI image and a basic grub.cfg
truncate -s $((20*1024*1024)) bootloader/efi.img
cp -rfv /arm/grub/efi/* $WORKDIR/tmpefi
mkfs.fat -F16 -n COS_GRUB bootloader/efi.img
mcopy -s -i bootloader/efi.img $WORKDIR/tmpefi/EFI ::EFI
mkdir -p ${STATEDIR}/cOS
dd if=/dev/zero of=${STATEDIR}/cOS/active.img bs=1M count=$default_active_size
mkfs.ext2 ${STATEDIR}/cOS/active.img -L ${ACTIVE_LABEL}
LOOP=$(losetup --show -f ${STATEDIR}/cOS/active.img)
if [ -z "$LOOP" ]; then
echo "No device"
exit 1
fi
mount -t ext2 $LOOP $TARGET
ensure_dir_structure $TARGET
# Download the container image
if [ -z "$directory" ]; then
echo ">>> Downloading container image"
luet util unpack $container_image $TARGET
else
echo ">>> Copying files from $directory"
rsync -axq --exclude='host' --exclude='mnt' --exclude='proc' --exclude='sys' --exclude='dev' --exclude='tmp' ${directory}/ $TARGET
fi
umount $TARGET
sync
losetup -d $LOOP
echo ">> Preparing passive.img"
cp -rfv ${STATEDIR}/cOS/active.img ${STATEDIR}/cOS/passive.img
tune2fs -L ${PASSIVE_LABEL} ${STATEDIR}/cOS/passive.img
# Preparing recovery
echo ">> Preparing recovery.img"
RECOVERY=$(mktemp -d --tmpdir arm-builder.XXXXXXXXXX)
mkdir -p ${RECOVERY}/cOS
cp -rfv ${STATEDIR}/cOS/active.img ${RECOVERY}/cOS/recovery.img
tune2fs -L ${SYSTEM_LABEL} ${RECOVERY}/cOS/recovery.img
# Install real grub config to recovery
cp -rfv /arm/grub/config/* $RECOVERY
mkdir -p $RECOVERY/grub2
cp -rfv /arm/grub/artifacts/* $RECOVERY/grub2
dd if=/dev/zero of=recovery_partition.img bs=1M count=$recovery_size
dd if=/dev/zero of=state_partition.img bs=1M count=$state_size
mkfs.ext4 -F -L ${RECOVERY_LABEL} recovery_partition.img
LOOP=$(losetup --show -f recovery_partition.img)
mkdir -p $WORKDIR/recovery
mount $LOOP $WORKDIR/recovery
cp -arf $RECOVERY/* $WORKDIR/recovery
umount $WORKDIR/recovery
losetup -d $LOOP
mkfs.ext4 -F -L ${STATE_LABEL} state_partition.img
LOOP=$(losetup --show -f state_partition.img)
mkdir -p $WORKDIR/state
mount $LOOP $WORKDIR/state
cp -arf $STATEDIR/* $WORKDIR/state
grub2-editenv $WORKDIR/state/grub_oem_env set "default_menu_entry=Kairos"
umount $WORKDIR/state
losetup -d $LOOP
cp -rfv state_partition.img bootloader/
cp -rfv recovery_partition.img bootloader/
## Optional, prepare COS_OEM and COS_PERSISTENT
# Create the grubenv forcing first boot to be on recovery system
mkdir -p $WORKDIR/oem
cp -rfv /defaults.yaml $WORKDIR/oem/01_defaults.yaml
# Create a 64MB filesystem for OEM volume
truncate -s $((64*1024*1024)) bootloader/oem.img
mkfs.ext2 -L "${OEM_LABEL}" -d $WORKDIR/oem bootloader/oem.img
# Create a 2GB filesystem for COS_PERSISTENT volume
truncate -s $((2048*1024*1024)) bootloader/persistent.img
mkfs.ext2 -L "${PERSISTENT_LABEL}" bootloader/persistent.img

81
tools-image/raw-images.sh Executable file
View File

@@ -0,0 +1,81 @@
#!/bin/bash
# Generates EFI bootable images (statically)
# This is a re-adaptation of https://github.com/rancher/elemental-toolkit/blob/v0.8.10-1/images/img-builder.sh, which was dropped
# How to use:
# First extract the image which you want to create an image from:
### luet util unpack <image> rootfs
# Then convert it to a raw disk (EFI only):
### docker run -v $PWD:/output --entrypoint /raw-images.sh -ti --rm test-image /output/rootfs /output/foo.raw cloud-init.yaml
: "${OEM_LABEL:=COS_OEM}"
: "${RECOVERY_LABEL:=COS_RECOVERY}"
: "${EXTEND:=}"
: "${RECOVERY_SIZE:=2048}"
DIRECTORY=$1
OUT=${2:-disk.raw}
CONFIG=$3
echo "Output: $OUT"
set -e
mkdir -p /build/root/grub2
mkdir /build/root/cOS
mkdir /build/efi
cp -rf /raw/grub/* /build/efi
cp -rf /raw/grubconfig/* /build/root
cp -rf /raw/grubartifacts/* /build/root/grub2
echo "Generating squashfs from $DIRECTORY"
mksquashfs $DIRECTORY recovery.squashfs -b 1024k -comp xz -Xbcj x86
mv recovery.squashfs /build/root/cOS/recovery.squashfs
grub2-editenv /build/root/grub_oem_env set "default_menu_entry=Kairos"
# Create a 2GB filesystem for RECOVERY including the contents for root (grub config and squasfs container)
# shellcheck disable=SC2004
truncate -s $(($RECOVERY_SIZE*1024*1024)) rootfs.part
mkfs.ext2 -L "${RECOVERY_LABEL}" -d /build/root rootfs.part
# Create the EFI partition FAT16 and include the EFI image and a basic grub.cfg
truncate -s $((20*1024*1024)) efi.part
mkfs.fat -F16 -n COS_GRUB efi.part
mcopy -s -i efi.part /build/efi/EFI ::EFI
# Create the grubenv forcing first boot to be on recovery system
mkdir -p /build/oem
cp /build/root/etc/cos/grubenv_firstboot /build/oem/grubenv
if [ -n "$CONFIG" ]; then
echo "Copying config file ($CONFIG)"
cp $CONFIG /build/oem
fi
# Create a 64MB filesystem for OEM volume
truncate -s $((64*1024*1024)) oem.part
mkfs.ext2 -L "${OEM_LABEL}" -d /build/oem oem.part
echo "Generating image $OUT"
# Create disk image, add 3MB of initial free space to disk, 1MB is for proper alignement, 2MB are for the hybrid legacy boot.
truncate -s $((3*1024*1024)) $OUT
{
cat efi.part
cat oem.part
cat rootfs.part
} >> $OUT
# Add an extra MB at the end of the disk for the gpt headers, in fact 34 sectors would be enough, but adding some more does not hurt.
truncate -s "+$((1024*1024))" $OUT
if [ -n "$EXTEND" ]; then
echo "Extending image of $EXTEND MB"
truncate -s "+$(($EXTEND*1024*1024))" $OUT
fi
# Create the partition table in $OUT (assumes sectors of 512 bytes)
sgdisk -n 1:2048:+2M -c 1:legacy -t 1:EF02 $OUT
sgdisk -n 2:0:+20M -c 2:UEFI -t 2:EF00 $OUT
sgdisk -n 3:0:+64M -c 3:oem -t 3:8300 $OUT
sgdisk -n 4:0:+${RECOVERY_SIZE}M -c 4:root -t 4:8300 $OUT