mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 17:23:00 +02:00
Compare commits
23
Commits
v25.06.0
...
upload-image
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc0edaf59c | ||
|
|
60b3a97f0e | ||
|
|
e534c0bd2d | ||
|
|
692fa166d1 | ||
|
|
c5d9a7c8b3 | ||
|
|
45c588567d | ||
|
|
2a8580edcc | ||
|
|
ffd0c05f41 | ||
|
|
9271c2439f | ||
|
|
7295077f27 | ||
|
|
b3d76de8f3 | ||
|
|
56f14ff520 | ||
|
|
9845e3b0c7 | ||
|
|
08e6833266 | ||
|
|
df54982978 | ||
|
|
efd6cce842 | ||
|
|
1dc84af095 | ||
|
|
d6c0d4bea7 | ||
|
|
85c4b579d3 | ||
|
|
1f268a7b90 | ||
|
|
562fd253c4 | ||
|
|
be547ba9a8 | ||
|
|
eabaaac7c2 |
+55
-151
@@ -1,30 +1,51 @@
|
||||
name: Bob the Builder
|
||||
name: Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
minimal:
|
||||
description: 'Build minimal defconfigs'
|
||||
flavor:
|
||||
description: 'Optional build flavor (e.g. _minimal)'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
parallell:
|
||||
default: ''
|
||||
type: string
|
||||
parallel:
|
||||
description: 'Massive parallel build of each image'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
infix_repo:
|
||||
description: 'Repo to checkout (for spin overrides)'
|
||||
required: false
|
||||
default: kernelkit/infix
|
||||
type: string
|
||||
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
name:
|
||||
required: true
|
||||
type: string
|
||||
flavor:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
infix_repo:
|
||||
required: false
|
||||
type: string
|
||||
default: kernelkit/infix
|
||||
|
||||
env:
|
||||
FLV: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.flavor || inputs.flavor }}
|
||||
INFIX_REPO: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.infix_repo || inputs.infix_repo }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Infix ${{ matrix.target }}
|
||||
name: Build ${{ inputs.name }} ${{ inputs.target }}
|
||||
runs-on: [ self-hosted, latest ]
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64, x86_64]
|
||||
fail-fast: false
|
||||
outputs:
|
||||
build_id: ${{ steps.vars.outputs.INFIX_BUILD_ID }}
|
||||
@@ -36,8 +57,11 @@ jobs:
|
||||
rm -rf ./.??* || true
|
||||
ls -la ./
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Checkout infix repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.INFIX_REPO }}
|
||||
ref: ${{ github.ref }}
|
||||
clean: true
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
@@ -55,26 +79,13 @@ jobs:
|
||||
"${{ github.event.number }}" "${{ github.event.pull_request.head.sha }}" \
|
||||
| tee -a $GITHUB_OUTPUT $GITHUB_ENV
|
||||
fi
|
||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||
if [ "${{ github.event.inputs.minimal }}" == "true" ]; then
|
||||
flavor="_minimal"
|
||||
fi
|
||||
else
|
||||
# Ensure 'release' job get the proper image when building main
|
||||
if [ "$GITHUB_REF_NAME" != "main" ]; then
|
||||
flavor="_minimal"
|
||||
else
|
||||
flavor=""
|
||||
fi
|
||||
if ${{ contains(github.event.pull_request.labels.*.name, 'ci:main') }}; then
|
||||
flavor=""
|
||||
fi
|
||||
fi
|
||||
target=${{ matrix.target }}
|
||||
echo "dir=infix-$target" >> $GITHUB_OUTPUT
|
||||
echo "tgz=infix-$target.tar.gz" >> $GITHUB_OUTPUT
|
||||
echo "flv=$flavor" >> $GITHUB_OUTPUT
|
||||
echo "Building target ${target}${flavor}_defconfig"
|
||||
|
||||
target=${{ inputs.target }}
|
||||
name=${{ inputs.name }}
|
||||
echo "dir=${name}-${target}" >> $GITHUB_OUTPUT
|
||||
echo "tgz=${name}-${target}.tar.gz" >> $GITHUB_OUTPUT
|
||||
echo "flv=$FLV" >> $GITHUB_OUTPUT
|
||||
echo "Building target ${target}${FLV}_defconfig"
|
||||
|
||||
- name: Restore Cache of dl/
|
||||
uses: actions/cache@v4
|
||||
@@ -88,16 +99,16 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .ccache/
|
||||
key: ccache-${{ matrix.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
key: ccache-${{ inputs.target }}-${{ hashFiles('.git/modules/buildroot/HEAD', 'package/*/*.hash') }}
|
||||
restore-keys: |
|
||||
ccache-${{ matrix.target }}-
|
||||
ccache-${{ inputs.target }}-
|
||||
ccache-
|
||||
|
||||
- name: Configure ${{ matrix.target }}${{ steps.vars.outputs.flv }}
|
||||
- name: Configure ${{ inputs.target }}${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
make ${{ matrix.target }}${{ steps.vars.outputs.flv }}_defconfig
|
||||
make ${{ inputs.target }}${{ steps.vars.outputs.flv }}_defconfig
|
||||
|
||||
- name: Unit Test ${{ matrix.target }}
|
||||
- name: Unit Test ${{ inputs.target }}
|
||||
run: |
|
||||
make test-unit
|
||||
|
||||
@@ -115,9 +126,9 @@ jobs:
|
||||
fi
|
||||
echo "MAKE=$MAKE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build ${{ matrix.target }}${{ steps.vars.outputs.flv }}
|
||||
- name: Build ${{ inputs.target }}${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
echo "Building ${{ matrix.target }}${{ steps.vars.outputs.flv }}_defconfig ..."
|
||||
echo "Building ${{ inputs.target }}${{ steps.vars.outputs.flv }}_defconfig ..."
|
||||
eval "${{ steps.parallel.outputs.MAKE }}"
|
||||
|
||||
- name: Check SBOM from Build
|
||||
@@ -139,7 +150,7 @@ jobs:
|
||||
printf "Size of output/images/: "
|
||||
ls -l output/images/
|
||||
|
||||
- name: Prepare ${{ matrix.target }} Artifact
|
||||
- name: Prepare ${{ inputs.target }} Artifact
|
||||
run: |
|
||||
cd output/
|
||||
mv images ${{ steps.vars.outputs.dir }}
|
||||
@@ -149,111 +160,4 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: output/${{ steps.vars.outputs.tgz }}
|
||||
name: artifact-${{ matrix.target }}
|
||||
|
||||
test:
|
||||
name: Regression Test of Infix x86_64
|
||||
needs: build
|
||||
runs-on: [ self-hosted, regression ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
clean: true
|
||||
submodules: recursive
|
||||
|
||||
- name: Set Build Variables
|
||||
id: vars
|
||||
run: |
|
||||
if [ -n "${{ needs.build.outputs.build_id }}" ]; then
|
||||
echo "INFIX_BUILD_ID=${{ needs.build.outputs.build_id }}" \
|
||||
>>$GITHUB_ENV
|
||||
fi
|
||||
|
||||
if [ "$GITHUB_REF_NAME" != "main" ]; then
|
||||
flavor="_minimal"
|
||||
else
|
||||
flavor=""
|
||||
fi
|
||||
echo "flv=$flavor" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure x86_64${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
make x86_64${{ steps.vars.outputs.flv }}_defconfig
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "artifact-*"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Restore x86-64${{ steps.vars.outputs.flv }} output/
|
||||
run: |
|
||||
ls -l
|
||||
mkdir -p output
|
||||
mv infix-x86_64.tar.gz output/
|
||||
cd output/
|
||||
tar xf infix-x86_64.tar.gz
|
||||
ln -s infix-x86_64 images
|
||||
|
||||
- name: Regression Test x86_64${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
make test
|
||||
|
||||
- name: Publish Test Result for x86_64${{ steps.vars.outputs.flv }}
|
||||
# Ensure this runs even if Regression Test fails
|
||||
if: always()
|
||||
run: cat test/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Generate Test Report for x86_64${{ steps.vars.outputs.flv }}
|
||||
# Ensure this runs even if Regression Test fails
|
||||
if: always()
|
||||
run: |
|
||||
asciidoctor-pdf \
|
||||
--theme test/9pm/report/theme.yml \
|
||||
-a pdf-fontsdir=test/9pm/report/fonts \
|
||||
test/.log/last/report.adoc \
|
||||
-o test/.log/last/report.pdf
|
||||
|
||||
- name: Upload Test Report as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-report
|
||||
path: test/.log/last/report.pdf
|
||||
|
||||
release:
|
||||
if: ${{github.repository_owner == 'kernelkit' && github.ref_name == 'main'}}
|
||||
name: Upload Latest Build
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "artifact-*"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create checksums ...
|
||||
run: |
|
||||
for file in *.tar.gz; do
|
||||
sha256sum $file > $file.sha256
|
||||
done
|
||||
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
omitName: true
|
||||
omitBody: true
|
||||
omitBodyDuringUpdate: true
|
||||
prerelease: true
|
||||
tag: "latest"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: "*.tar.gz*"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
cat <<EOF >> $GITHUB_STEP_SUMMARY
|
||||
# Latest Build Complete! :rocket:
|
||||
|
||||
For the public download links of these build artifacts, please see:
|
||||
<https://github.com/kernelkit/infix/releases/tag/latest>
|
||||
EOF
|
||||
name: artifact-${{ inputs.target }}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: Publish latest Infix
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Upload Latest Build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "artifact-*"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create checksums ...
|
||||
run: |
|
||||
for file in *.tar.gz; do
|
||||
sha256sum $file > $file.sha256
|
||||
done
|
||||
|
||||
- uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
omitName: true
|
||||
omitBody: true
|
||||
omitBodyDuringUpdate: true
|
||||
prerelease: true
|
||||
tag: "latest"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
artifacts: "*.tar.gz*"
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
cat <<EOF >> $GITHUB_STEP_SUMMARY
|
||||
# Latest Build Complete! :rocket:
|
||||
|
||||
For the public download links of these build artifacts, please see:
|
||||
<https://github.com/kernelkit/infix/releases/tag/latest>
|
||||
EOF
|
||||
@@ -0,0 +1,116 @@
|
||||
name: Test Infix
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
infix_repo:
|
||||
description: 'Repo to checkout (for spin overrides)'
|
||||
required: false
|
||||
default: kernelkit/infix
|
||||
type: string
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
target:
|
||||
required: true
|
||||
type: string
|
||||
name:
|
||||
required: true
|
||||
type: string
|
||||
flavor:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
infix_repo:
|
||||
required: false
|
||||
type: string
|
||||
default: kernelkit/infix
|
||||
ninepm-conf:
|
||||
required: false
|
||||
type: string
|
||||
default: ''
|
||||
test-path:
|
||||
required: false
|
||||
type: string
|
||||
default: 'test'
|
||||
|
||||
env:
|
||||
FLV: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.flavor || inputs.flavor }}
|
||||
INFIX_REPO: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.infix_repo || inputs.infix_repo }}
|
||||
NINEPM_CONF: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ninepm-conf || inputs.ninepm-conf }}
|
||||
TEST_PATH: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.test-path || inputs.test-path }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Regression Test ${{ inputs.name }} ${{ inputs.target }}
|
||||
runs-on: [ self-hosted, regression ]
|
||||
steps:
|
||||
- name: Checkout infix repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ env.INFIX_REPO }}
|
||||
ref: ${{ github.ref }}
|
||||
clean: true
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Set Build Variables
|
||||
id: vars
|
||||
run: |
|
||||
if [ -n "${{ needs.build.outputs.build_id }}" ]; then
|
||||
echo "INFIX_BUILD_ID=${{ needs.build.outputs.build_id }}" \
|
||||
>>$GITHUB_ENV
|
||||
fi
|
||||
|
||||
echo "flv=$FLV" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Configure ${{ inputs.target }}${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
make ${{ inputs.target }}${{ steps.vars.outputs.flv }}_defconfig
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: "artifact-*"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Restore x86-64${{ steps.vars.outputs.flv }} output/
|
||||
run: |
|
||||
target=${{ inputs.target }}
|
||||
name=${{ inputs.name }}
|
||||
|
||||
ls -l
|
||||
mkdir -p output
|
||||
mv ${name}-${target}.tar.gz output/
|
||||
cd output/
|
||||
tar xf ${name}-${target}.tar.gz
|
||||
ln -s ${name}-${target} images
|
||||
|
||||
- name: Regression Test x86_64${{ steps.vars.outputs.flv }}
|
||||
run: |
|
||||
if [ -n "$NINEPM_CONF" ]; then
|
||||
export NINEPM_PROJ_CONFIG="${GITHUB_WORKSPACE}/$NINEPM_CONF"
|
||||
echo "DEBUG: NINEPM_PROJ_CONFIG is '$NINEPM_PROJ_CONFIG'"
|
||||
fi
|
||||
make test
|
||||
|
||||
- name: Publish Test Result for x86_64${{ steps.vars.outputs.flv }}
|
||||
# Ensure this runs even if Regression Test fails
|
||||
if: always()
|
||||
run: cat $TEST_PATH/.log/last/result-gh.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Generate Test Report for x86_64${{ steps.vars.outputs.flv }}
|
||||
# Ensure this runs even if Regression Test fails
|
||||
if: always()
|
||||
run: |
|
||||
asciidoctor-pdf \
|
||||
--theme $TEST_PATH/9pm/report/theme.yml \
|
||||
-a pdf-fontsdir=$TEST_PATH/9pm/report/fonts \
|
||||
$TEST_PATH/.log/last/report.adoc \
|
||||
-o $TEST_PATH/.log/last/report.pdf
|
||||
|
||||
- name: Upload Test Report as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-report
|
||||
path: ${{ env.TEST_PATH }}/.log/last/report.pdf
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
name: Self Trigger
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- ci-workflow-redesign
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-x86_64:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
target: "x86_64"
|
||||
name: "infix"
|
||||
flavor: "_minimal"
|
||||
|
||||
build-aarch64:
|
||||
uses: ./.github/workflows/build.yml
|
||||
with:
|
||||
target: "aarch64"
|
||||
name: "infix"
|
||||
flavor: "_minimal"
|
||||
|
||||
test-run-x86_64:
|
||||
needs: build-x86_64
|
||||
uses: ./.github/workflows/test.yml
|
||||
with:
|
||||
target: "x86_64"
|
||||
name: "infix"
|
||||
|
||||
test-publish-x86_64:
|
||||
needs: test-run-x86_64
|
||||
uses: ./.github/workflows/publish.yml
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# /telemetry/optics is for streaming (not used atm)
|
||||
# Proxy buffer settings for large files
|
||||
proxy_buffering off; # Disable buffering for streaming
|
||||
proxy_request_buffering off; # Stream request body immediately
|
||||
proxy_max_temp_file_size 0; # No temp files
|
||||
location ~ ^/(restconf|yang|.well-known)/ {
|
||||
client_max_body_size 200M;
|
||||
client_body_buffer_size 1M;
|
||||
grpc_pass grpc://[::1]:10080;
|
||||
grpc_set_header Host $host;
|
||||
grpc_set_header X-Real-IP $remote_addr;
|
||||
|
||||
+177
-16
@@ -168,36 +168,36 @@ Now you can rebuild `confd`, just as described above, and restart Infix:
|
||||
|
||||
### `statd`
|
||||
|
||||
The Infix status daemon, `src/statd`, is responsible for populating the
|
||||
sysrepo `operational` datastore. Like `confd`, it uses XPath subscriptions,
|
||||
but unlike `confd`, it relies entirely on `yanger`, a Python script that
|
||||
The Infix status daemon, `src/statd`, is responsible for populating the
|
||||
sysrepo `operational` datastore. Like `confd`, it uses XPath subscriptions,
|
||||
but unlike `confd`, it relies entirely on `yanger`, a Python script that
|
||||
gathers data from local linux services and feeds it into sysrepo.
|
||||
|
||||
To apply changes, rebuild the image:
|
||||
|
||||
make python-statd-rebuild statd-rebuild all
|
||||
|
||||
Rebuilding the image and testing on target for every change during
|
||||
development process can be tedious. Instead, `yanger` allows remote
|
||||
execution, running the script directly on the host system (test
|
||||
Rebuilding the image and testing on target for every change during
|
||||
development process can be tedious. Instead, `yanger` allows remote
|
||||
execution, running the script directly on the host system (test
|
||||
container):
|
||||
|
||||
infamy0:test # ../src/statd/python/yanger/yanger -x "../utils/ixll -A ssh d3a" ieee802-dot1ab-lldp
|
||||
|
||||
`ixll` is a utility script that lets you run network commands using an
|
||||
**interface name** instead of a hostname. It makes operations like
|
||||
**interface name** instead of a hostname. It makes operations like
|
||||
`ssh`, `scp`, and network discovery easier.
|
||||
|
||||
Normally, `yanger` runs commands **locally** to retrieve data
|
||||
(e.g., `lldpcli` when handling `ieee802-dot1ab-lldp`). However, when
|
||||
executed with `-x "../utils/ixll -A ssh d3a"` it redirects these
|
||||
commands to a remote system connected to the local `d3a` interface via
|
||||
SSH. This setup is used for running `yanger` in an
|
||||
Normally, `yanger` runs commands **locally** to retrieve data
|
||||
(e.g., `lldpcli` when handling `ieee802-dot1ab-lldp`). However, when
|
||||
executed with `-x "../utils/ixll -A ssh d3a"` it redirects these
|
||||
commands to a remote system connected to the local `d3a` interface via
|
||||
SSH. This setup is used for running `yanger` in an
|
||||
[interactive test environment](testing.md#interactive-usage). The yanger
|
||||
script runs on the `host` system, but key commands are executed on the
|
||||
script runs on the `host` system, but key commands are executed on the
|
||||
`target` system.
|
||||
|
||||
For debugging or testing, you can capture system command output and
|
||||
For debugging or testing, you can capture system command output and
|
||||
replay it later without needing a live system.
|
||||
|
||||
To capture:
|
||||
@@ -211,12 +211,173 @@ To replay:
|
||||
This is especially useful when working in isolated environments or debugging
|
||||
issues without direct access to the DUT.
|
||||
|
||||
### Upgrading Packages
|
||||
|
||||
#### Buildroot
|
||||
|
||||
Kernelkit maintains an internal [fork of
|
||||
Buildroot](https://github.com/kernelkit/buildroot), with branches
|
||||
following the naming scheme `YYYY.MM.patch-kkit`
|
||||
e.g. `2025.02.1-kkit`, which means a new branch should be created
|
||||
whenever Buildroot is updated. These branches should contain **only**
|
||||
changes to existing packages (but no new patches), modifications to
|
||||
Buildroot itself or upstream backports.
|
||||
|
||||
KernelKit track the latest Buildroot LTS (Long-Term Support) release
|
||||
and updates. The upgrade of LTS minor releases is expected to have low
|
||||
impact and should be done as soon there is a patch release of
|
||||
Buildroot LTS is available.
|
||||
|
||||
> **Depending on your setup, follow the appropriate steps below.**
|
||||
|
||||
🔁 If you **already have** the Buildroot repo locally
|
||||
|
||||
1. Navigate to the Buildroot directory
|
||||
```bash
|
||||
$ cd buildroot
|
||||
```
|
||||
2. Pull the latest changes from KernelKit
|
||||
```bash
|
||||
$ git pull
|
||||
```
|
||||
3. Fetch the latest tags from upstream
|
||||
```bash
|
||||
$ git fetch upstream --tags
|
||||
```
|
||||
|
||||
|
||||
🆕 If you don't have the repo locally
|
||||
|
||||
1. Clone the Kernelkit Buildroot repository
|
||||
```bash
|
||||
$ git clone git@github.com:kernelkit/buildroot.git
|
||||
```
|
||||
|
||||
2. Add the upstream remote
|
||||
```bash
|
||||
$ git remote add upstream https://gitlab.com/buildroot.org/buildroot.git
|
||||
```
|
||||
3. Checkout old KernelKit branch
|
||||
```bash
|
||||
$ git checkout 2025.02.1-kkit
|
||||
```
|
||||
|
||||
|
||||
🛠 Continue from here (applies to both cases):
|
||||
|
||||
4. Create a new branch based on the **previous** KernelKit Buildroot
|
||||
release (e.g. `2025.02.1-kkit`) and name it according to the naming scheme (e.g. `2025.02.2-kkit`)
|
||||
```bash
|
||||
$ git checkout -b 2025.02.2-kkit
|
||||
```
|
||||
5. Rebase the new branch onto the corresponding upstream release
|
||||
```bash
|
||||
$ git rebase 2025.02.2
|
||||
```
|
||||
> [!NOTE] It is **not** allowed to rebase the branch when bumped in Infix.
|
||||
|
||||
6. Push the new branch and tags
|
||||
```bash
|
||||
$ git push origin 2025.02.2-kkit --tags
|
||||
```
|
||||
7. In Infix, checkout new branch of Buildroot
|
||||
```bash
|
||||
$ cd buildroot
|
||||
$ git fetch
|
||||
$ git checkout 2025.02.2-kkit
|
||||
```
|
||||
8. Push changes
|
||||
Commit and push the changes. Don’t forget to update the changelog.
|
||||
|
||||
9. Create a pull request.
|
||||
|
||||
> [!NOTE] Remember to set the pull request label to `ci:main` to ensure full CI coverage.
|
||||
|
||||
|
||||
#### Linux kernel
|
||||
|
||||
KernelKit maintains an internal [fork of Linux
|
||||
kernel](https://github.com/kernelkit/linux), with branches following
|
||||
the naming scheme `kkit-linux-[version].y`, e.g. `kkit-6.12.y`, which
|
||||
means a new branch should be created whenever the major kernel version
|
||||
is updated. This branch should contain *all* kernel patches used by
|
||||
Infix.
|
||||
|
||||
KernelKit track the latest Linux kernel LTS (Long-Term Support)
|
||||
release and updates. The upgrade of LTS minor releases is expected to
|
||||
have low impact and should be done as soon as a patch release of the
|
||||
LTS Linux kernel is available.
|
||||
|
||||
|
||||
🔁 If you **already have** the Linux kernel repo locally
|
||||
|
||||
1. Navigate to the Linux kernel directory
|
||||
```bash
|
||||
$ cd linux
|
||||
```
|
||||
2. Get latest changes from KernelKit
|
||||
```bash
|
||||
$ git pull
|
||||
```
|
||||
3. Fetch the latest tags from upstream
|
||||
```bash
|
||||
$ git fetch upstream --tags
|
||||
```
|
||||
|
||||
🆕 If you don't have the repo locally
|
||||
|
||||
1. Clone the KernelKit Linux kernel repository
|
||||
```bash
|
||||
$ git clone git@github.com:kernelkit/linux.git
|
||||
```
|
||||
2. Add the upstream remote
|
||||
```bash
|
||||
$ git remote add upstream git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
|
||||
```
|
||||
|
||||
3. Checkout correct kernel branch
|
||||
```bash
|
||||
$ git checkout kkit-linux-6.12.y
|
||||
```
|
||||
|
||||
🛠 Continue from here (applies to both cases)
|
||||
|
||||
|
||||
4. Rebase on the upstream release
|
||||
```bash
|
||||
$ git rebase v6.12.29
|
||||
```
|
||||
|
||||
6. Push changes and the tags
|
||||
```bash
|
||||
|
||||
$ git push -f origin kkit-linux-6.12.y --tags
|
||||
```
|
||||
|
||||
**Move to your infix directory**
|
||||
|
||||
7. Generate patches
|
||||
```bash
|
||||
$ make x86_64_defconfig
|
||||
$ cd output
|
||||
$ ../utils/kernel-refresh.sh -k /path/to/linux -o 6.12.28 -t v6.12.29
|
||||
```
|
||||
> [!NOTE] See help of `kernel-refresh.sh` script for more information
|
||||
|
||||
|
||||
8. Push changes
|
||||
Commit and push the changes. Don’t forget to update the s:changelog:doc/ChangeLog.md.
|
||||
|
||||
9. Create a pull request.
|
||||
> [!NOTE] Remember to set the pull request label to `ci:main` to ensure full CI coverage.
|
||||
|
||||
|
||||
### Agree on YANG Model
|
||||
|
||||
When making changes to the `confd` and `statd` services, you will often need to update
|
||||
the YANG models. If you are adding a new YANG module, it's best to follow the
|
||||
the YANG models. If you are adding a new YANG module, it's best to follow the
|
||||
structure of an existing one. However, before making any changes, **always discuss
|
||||
them with the Infix core team**. This helps avoid issues later in development and
|
||||
them with the Infix core team**. This helps avoid issues later in development and
|
||||
makes pull request reviews smoother.
|
||||
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
|
||||
PKG_CHECK_MODULES([libite], [libite >= 2.6.1])
|
||||
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
|
||||
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
|
||||
PKG_CHECK_MODULES([libssl], [libssl >= 1.0.0])
|
||||
|
||||
# Control build with automake flags
|
||||
AM_CONDITIONAL(CONTAINERS, [test "x$enable_containers" != "xno"])
|
||||
|
||||
@@ -12,6 +12,7 @@ confd_plugin_la_CFLAGS = \
|
||||
$(libite_CFLAGS) \
|
||||
$(sysrepo_CFLAGS) \
|
||||
$(libsrx_CFLAGS) \
|
||||
$(libssl_CFLAGS) \
|
||||
$(CFLAGS)
|
||||
|
||||
confd_plugin_la_LIBADD = \
|
||||
@@ -20,7 +21,8 @@ confd_plugin_la_LIBADD = \
|
||||
$(jansson_LIBS) \
|
||||
$(libite_LIBS) \
|
||||
$(sysrepo_LIBS) \
|
||||
$(libsrx_LIBS)
|
||||
$(libsrx_LIBS) \
|
||||
$(libssl_LIBS)
|
||||
|
||||
confd_plugin_la_SOURCES = \
|
||||
base64.c base64.h \
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "rauc-installer.h"
|
||||
@@ -21,24 +24,111 @@ static RaucInstaller *infix_system_sw_new_rauc(void)
|
||||
return rauc;
|
||||
}
|
||||
|
||||
static int base64_decode_inplace(char *input, size_t input_len, size_t *output_len)
|
||||
{
|
||||
BIO *bio, *b64;
|
||||
int decode_len;
|
||||
|
||||
bio = BIO_new_mem_buf(input, input_len);
|
||||
if (!bio) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
if (!b64) {
|
||||
BIO_free(bio);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
bio = BIO_push(b64, bio);
|
||||
|
||||
/* Decode directly into the same buffer */
|
||||
decode_len = BIO_read(bio, input, input_len);
|
||||
BIO_free_all(bio);
|
||||
|
||||
if (decode_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*output_len = decode_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
const char *path, const sr_val_t *input,
|
||||
const size_t input_cnt, sr_event_t event,
|
||||
unsigned request_id, sr_val_t **output,
|
||||
size_t *output_cnt, void *priv)
|
||||
{
|
||||
char *url = input->data.string_val;
|
||||
sr_error_t srerr = SR_ERR_OK;
|
||||
GError *raucerr = NULL;
|
||||
RaucInstaller *rauc;
|
||||
RaucInstaller *rauc = NULL;
|
||||
char *install_source = NULL;
|
||||
|
||||
DEBUG("url:%s", url);
|
||||
const char *url = NULL;
|
||||
char *binary_data = NULL;
|
||||
size_t binary_len = 0;
|
||||
size_t decoded_len = 0;
|
||||
|
||||
for (size_t i = 0; i < input_cnt; i++) {
|
||||
if (strcmp(input[i].xpath, "/infix-system:install-bundle/url") == 0) {
|
||||
if (input[i].type == SR_STRING_T) {
|
||||
url = input[i].data.string_val;
|
||||
}
|
||||
} else if (strcmp(input[i].xpath, "/infix-system:install-bundle/image") == 0) {
|
||||
if (input[i].type == SR_BINARY_T) {
|
||||
binary_data = (char *)input[i].data.binary_val; // Cast away const for in-place decode
|
||||
binary_len = strlen(binary_data); // Length of base64 string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (url) {
|
||||
DEBUG("Installing from URL: %s", url);
|
||||
install_source = (char *)url;
|
||||
|
||||
} else if (binary_data) {
|
||||
const char *temp_dir = "/tmp";
|
||||
char path[256];
|
||||
FILE *fp;
|
||||
|
||||
DEBUG("Installing from uploaded binary data (%zu bytes base64)", binary_len);
|
||||
|
||||
if (base64_decode_inplace(binary_data, binary_len, &decoded_len) != 0) {
|
||||
sr_session_set_netconf_error(session, "application", "invalid-value",
|
||||
NULL, NULL, "Failed to decode base64 image data", 0);
|
||||
srerr = SR_ERR_INVAL_ARG;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fmkpath(0775, "%s/images", temp_dir);
|
||||
snprintf(path, sizeof(path), "%s/images/install_bundle", temp_dir);
|
||||
|
||||
fp = fopen(path, "wb");
|
||||
if (!fp) {
|
||||
ERROR("Could not open %s", path);
|
||||
return SR_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
fwrite(binary_data, sizeof(char), decoded_len, fp);
|
||||
fclose(fp);
|
||||
install_source = path;
|
||||
|
||||
} else {
|
||||
ERROR("Unknown source");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rauc = infix_system_sw_new_rauc();
|
||||
if (!rauc)
|
||||
return SR_ERR_INTERNAL;
|
||||
if (!rauc) {
|
||||
sr_session_set_netconf_error(session, "application", "operation-failed",
|
||||
NULL, NULL, "Failed to initialize RAUC installer", 0);
|
||||
srerr = SR_ERR_INTERNAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rauc_installer_call_install_sync(rauc, url, NULL, &raucerr);
|
||||
rauc_installer_call_install_sync(rauc, install_source, NULL, &raucerr);
|
||||
if (raucerr) {
|
||||
sr_session_set_netconf_error(session, "application", "operation-failed",
|
||||
NULL, NULL, raucerr->message, 0);
|
||||
@@ -46,13 +136,18 @@ static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
srerr = SR_ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
g_object_unref(rauc);
|
||||
cleanup:
|
||||
if (rauc) {
|
||||
g_object_unref(rauc);
|
||||
}
|
||||
|
||||
return srerr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
boot order can only be: primary, secondary and net, limited by model
|
||||
*/
|
||||
*/
|
||||
static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
const char *path, const sr_val_t *input,
|
||||
const size_t input_cnt, sr_event_t event,
|
||||
@@ -63,24 +158,24 @@ static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t su
|
||||
const sr_val_t *val = &input[i];
|
||||
|
||||
if (i != 0)
|
||||
strlcat(boot_order, " ", sizeof(boot_order));
|
||||
strlcat(boot_order, val->data.string_val, sizeof(boot_order));
|
||||
}
|
||||
strlcat(boot_order, " ", sizeof(boot_order));
|
||||
strlcat(boot_order, val->data.string_val, sizeof(boot_order));
|
||||
}
|
||||
|
||||
if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
|
||||
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in U-Boot");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else if (fexist("/mnt/aux/grub/grubenv")) {
|
||||
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in Grub");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
ERROR("No supported boot loader found");
|
||||
return SR_ERR_UNSUPPORTED;
|
||||
}
|
||||
if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
|
||||
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in U-Boot");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else if (fexist("/mnt/aux/grub/grubenv")) {
|
||||
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in Grub");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
ERROR("No supported boot loader found");
|
||||
return SR_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
@@ -204,16 +204,42 @@ submodule infix-system-software {
|
||||
description
|
||||
"Upgrade the system's software by installing the specified bundle.";
|
||||
input {
|
||||
leaf url {
|
||||
type string;
|
||||
choice source {
|
||||
mandatory true;
|
||||
description
|
||||
"The location of the software bundle, specified as a Uniform
|
||||
Resource Locator (URL). Currently supported protocols include
|
||||
FTP, HTTP(S) and SCP.";
|
||||
"Source of the software bundle to install.";
|
||||
case url {
|
||||
leaf url {
|
||||
type string;
|
||||
description
|
||||
"The location of the software bundle, specified as a Uniform
|
||||
Resource Locator (URL). Currently supported protocols include
|
||||
FTP, HTTP(S) and SCP.";
|
||||
}
|
||||
}
|
||||
|
||||
case upload {
|
||||
leaf image {
|
||||
type binary;
|
||||
description
|
||||
"The image data to install, provided as base64-encoded binary data.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc install-upload-bundle {
|
||||
nacm:default-deny-all;
|
||||
description "Upgrade the system's software by installing from a supplied blob.";
|
||||
input {
|
||||
leaf image {
|
||||
type binary;
|
||||
mandatory true;
|
||||
description "The image to install";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpc set-boot-order {
|
||||
nacm:default-deny-all;
|
||||
description
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
=== Interface Speed Duplex (Copper)
|
||||
==== Description
|
||||
Verify that auto-negotiation results in expected speed/duplex mode.
|
||||
Verify that the interface operates at the expected speed/duplex in two scenarios:
|
||||
|
||||
1. Fixed configuration – host and target are both manually set to a specific speed/duplex
|
||||
2. Auto-negotiation – host advertises selectable modes and the target negotiates
|
||||
to the highest common speed/duplex.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
@@ -17,17 +21,17 @@ endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Enable target interface
|
||||
. Set fixed 10/full
|
||||
. Set fixed 10/half
|
||||
. Set fixed 100/full
|
||||
. Set fixed 100/half
|
||||
. Verify fixed 10/full
|
||||
. Verify fixed 10/half
|
||||
. Verify fixed 100/full
|
||||
. Verify fixed 100/half
|
||||
. Switch to auto-negotiation mode for target and host
|
||||
. Configure host to advertise 10/Full only
|
||||
. Configure host to advertise 10/Half only
|
||||
. Configure host to advertise 100/Full only
|
||||
. Configure host to advertise 100/Half only
|
||||
. Configure host to advertise 10/half + 10/full + 100/half
|
||||
. Configure host to advertise 10/half + 10/full + 100/half + 100/full + 1000/full
|
||||
. Verify auto-negotiation to 10/Full only
|
||||
. Verify auto-negotiation to 10/Half only
|
||||
. Verify auto-negotiation to 100/Full only
|
||||
. Verify auto-negotiation to 100/Half only
|
||||
. Verify auto-negotiation to 10/half + 10/full + 100/half
|
||||
. Verify auto-negotiation to 10/half + 10/full + 100/half + 100/full + 1000/full
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
"""
|
||||
Interface Speed Duplex (Copper)
|
||||
|
||||
Verify that auto-negotiation results in expected speed/duplex mode.
|
||||
Verify that the interface operates at the expected speed/duplex in two scenarios:
|
||||
|
||||
1. Fixed configuration – host and target are both manually set to a specific speed/duplex
|
||||
2. Auto-negotiation – host advertises selectable modes and the target negotiates
|
||||
to the highest common speed/duplex.
|
||||
"""
|
||||
|
||||
import infamy
|
||||
@@ -164,22 +168,22 @@ with infamy.Test() as test:
|
||||
ns.addip("10.0.0.1")
|
||||
|
||||
# Fixed mode tests
|
||||
with test.step("Set fixed 10/full"):
|
||||
with test.step("Verify fixed 10/full"):
|
||||
set_host_speed_duplex(hdata, 10, "full")
|
||||
set_target_speed_duplex(target, tdata, 10, "full")
|
||||
verify_speed_duplex(target, ns, tdata, 10, "full")
|
||||
|
||||
with test.step("Set fixed 10/half"):
|
||||
with test.step("Verify fixed 10/half"):
|
||||
set_host_speed_duplex(hdata, 10, "half")
|
||||
set_target_speed_duplex(target, tdata, 10, "half")
|
||||
verify_speed_duplex(target, ns, tdata, 10, "half")
|
||||
|
||||
with test.step("Set fixed 100/full"):
|
||||
with test.step("Verify fixed 100/full"):
|
||||
set_host_speed_duplex(hdata, 100, "full")
|
||||
set_target_speed_duplex(target, tdata, 100, "full")
|
||||
verify_speed_duplex(target, ns, tdata, 100, "full")
|
||||
|
||||
with test.step("Set fixed 100/half"):
|
||||
with test.step("Verify fixed 100/half"):
|
||||
set_host_speed_duplex(hdata, 100, "half")
|
||||
set_target_speed_duplex(target, tdata, 100, "half")
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
@@ -189,27 +193,27 @@ with infamy.Test() as test:
|
||||
enable_host_autoneg(hdata)
|
||||
enable_target_autoneg(target, tdata)
|
||||
|
||||
with test.step("Configure host to advertise 10/Full only"):
|
||||
with test.step("Verify auto-negotiation to 10/Full only"):
|
||||
advertise_host_modes(hdata, ["10full"])
|
||||
verify_speed_duplex(target, ns, tdata, 10, "full")
|
||||
|
||||
with test.step("Configure host to advertise 10/Half only"):
|
||||
with test.step("Verify auto-negotiation to 10/Half only"):
|
||||
advertise_host_modes(hdata, ["10half"])
|
||||
verify_speed_duplex(target, ns, tdata, 10, "half")
|
||||
|
||||
with test.step("Configure host to advertise 100/Full only"):
|
||||
with test.step("Verify auto-negotiation to 100/Full only"):
|
||||
advertise_host_modes(hdata, ["100full"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "full")
|
||||
|
||||
with test.step("Configure host to advertise 100/Half only"):
|
||||
with test.step("Verify auto-negotiation to 100/Half only"):
|
||||
advertise_host_modes(hdata, ["100half"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
|
||||
with test.step("Configure host to advertise 10/half + 10/full + 100/half"):
|
||||
with test.step("Verify auto-negotiation to 10/half + 10/full + 100/half"):
|
||||
advertise_host_modes(hdata, ["10half", "10full", "100half"])
|
||||
verify_speed_duplex(target, ns, tdata, 100, "half")
|
||||
|
||||
with test.step("Configure host to advertise 10/half + 10/full + 100/half + 100/full + 1000/full"):
|
||||
with test.step("Verify auto-negotiation to 10/half + 10/full + 100/half + 100/full + 1000/full"):
|
||||
advertise_host_modes(hdata, ["10half", "10full", "100half", "100full", "1000full"])
|
||||
verify_speed_duplex(target, ns, tdata, 1000, "full")
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
container_host_commands.adoc
|
||||
@@ -0,0 +1,29 @@
|
||||
=== Host Command Execution from Container
|
||||
==== Description
|
||||
This test verifies that a container running on Infix can execute commands
|
||||
that affect the host system. Specifically, it confirms that the container
|
||||
can change the hostname of the host.
|
||||
|
||||
==== Topology
|
||||
ifdef::topdoc[]
|
||||
image::{topdoc}../../test/case/infix_containers/container_host_commands/topology.svg[Host Command Execution from Container topology]
|
||||
endif::topdoc[]
|
||||
ifndef::topdoc[]
|
||||
ifdef::testgroup[]
|
||||
image::container_host_commands/topology.svg[Host Command Execution from Container topology]
|
||||
endif::testgroup[]
|
||||
ifndef::testgroup[]
|
||||
image::topology.svg[Host Command Execution from Container topology]
|
||||
endif::testgroup[]
|
||||
endif::topdoc[]
|
||||
==== Test sequence
|
||||
. Set up topology and attach to target DUT
|
||||
. Set initial hostname
|
||||
. Verify initial hostname in operational
|
||||
. Include script in OCI image to modify host hostname
|
||||
. Verify container has started
|
||||
. Verify the new hostname set by the container
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x1 Pages: 1 -->
|
||||
<svg width="424pt" height="45pt"
|
||||
viewBox="0.00 0.00 424.03 45.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 41)">
|
||||
<title>1x1</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-41 420.03,-41 420.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 100,-36.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-36.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-36.5 416.03,-36.5 416.03,-0.5 300.03,-0.5"/>
|
||||
<text text-anchor="middle" x="325.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-36.5 "/>
|
||||
<text text-anchor="middle" x="383.03" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="lightgray" stroke-width="2" d="M100,-18.5C100,-18.5 300.03,-18.5 300.03,-18.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
Executable
+269
@@ -0,0 +1,269 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
usage()
|
||||
{
|
||||
cat <<EOF
|
||||
usage: $0 COMMAND [ARGS...]
|
||||
|
||||
Update contents of Infix images
|
||||
|
||||
Commands:
|
||||
|
||||
unpack PKG KEY-DIR DIR
|
||||
|
||||
Unpack the RAUC bundle PKG and the root filesystem contained in
|
||||
it, using the keys stored in KEY-DIR, and store the results in
|
||||
DIR.
|
||||
|
||||
pack DIR [KEY-DIR] PKG
|
||||
|
||||
Repackage the (possibly modified) root filesystem in DIR, updating
|
||||
all signatures as necessary using the keys stored in KEY-DIR (or
|
||||
the keys used to unpack the bundle, if not specified) and store
|
||||
the resulting bundle in PKG.
|
||||
|
||||
exec DIR COMMAND [ARGS...]
|
||||
|
||||
Run COMMAND, possibly with ARGS, from the root of the filesystem
|
||||
in DIR, inside of a fakeroot context that allows otherwise
|
||||
privileged operations such as ownership modifications.
|
||||
|
||||
help
|
||||
|
||||
Show this message and exit.
|
||||
|
||||
Key directories:
|
||||
|
||||
KEY-DIR arguments must reference a directory that contains the
|
||||
public/private keypair used to verify/sign an image. Most commonly,
|
||||
board/common/signing-keys/development from the Infix source tree is
|
||||
used to validate the input image.
|
||||
|
||||
Example:
|
||||
|
||||
Include a custom file inside a prebuilt Infix image, assign it to
|
||||
the UID/GID 1337; and package up the results, signing it with the
|
||||
same developer key that was used to create it:
|
||||
|
||||
ixbin unpack infix-aarch64.pkg ~/infix/board/common/signing-keys/development tmp
|
||||
cp VBRUN300.DLL tmp/rootfs/lib/
|
||||
ixbin exec tmp chown 1337:1337 lib/VBRUN300.DLL
|
||||
ixbin pack tmp infix-vb3-aarch64.pkg
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
msg()
|
||||
{
|
||||
printf "\e[37;44mixbin: %-80s\e[0m\n" "$*"
|
||||
}
|
||||
|
||||
err()
|
||||
{
|
||||
printf "\e[37;41mixbin: ERROR: %-73s\e[0m\n" "$*" >&2
|
||||
}
|
||||
|
||||
die()
|
||||
{
|
||||
err "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
ensuredeps()
|
||||
{
|
||||
local DEPS="fakeroot mksquashfs unsquashfs awk env mkimage truncate dtc fdtget rauc"
|
||||
local missing=
|
||||
|
||||
for dep in $DEPS; do
|
||||
command -v $dep >/dev/null && continue
|
||||
missing="$missing $dep"
|
||||
done
|
||||
|
||||
if [ -n "$missing" ]; then
|
||||
die "Missing dependencies:$missing"
|
||||
fi
|
||||
}
|
||||
|
||||
squnpack()
|
||||
{
|
||||
local sq="$WORKDIR"/pkg/rootfs.img
|
||||
|
||||
# Capture the original compression algo and block size for when we
|
||||
# resquash it later on.
|
||||
unsquashfs -s "$sq" | awk '
|
||||
/^Compression/ { printf(" -comp %s", $2); }
|
||||
/^Block size/ { printf(" -b %s", $3); }
|
||||
' >"$WORKDIR"/mksquash-opts
|
||||
|
||||
$FAKEROOT unsquashfs -d "$WORKDIR"/rootfs "$sq"
|
||||
}
|
||||
|
||||
sqexec()
|
||||
{
|
||||
$FAKEROOT env -C "$WORKDIR"/rootfs "$@"
|
||||
}
|
||||
|
||||
sqpack()
|
||||
{
|
||||
$FAKEROOT mksquashfs "$WORKDIR"/rootfs "$WORKDIR"/pkg/rootfs.img \
|
||||
$(cat "$WORKDIR"/mksquash-opts) -noappend "$@"
|
||||
}
|
||||
|
||||
sqsign()
|
||||
{
|
||||
local its="$1"
|
||||
local keys="$2"
|
||||
local hsize="$3"
|
||||
local out="$4"
|
||||
|
||||
mkimage -E -p "$hsize" -B "$hsize" -k "$keys" -f "$its" "$WORKDIR"/sign.itb
|
||||
truncate -s $(($hsize)) "$WORKDIR"/sign.itb
|
||||
mv "$WORKDIR"/sign.itb "$out"
|
||||
}
|
||||
|
||||
sqresign()
|
||||
{
|
||||
local keys="$1"
|
||||
local sq="$WORKDIR"/pkg/rootfs.img
|
||||
local itbh="$WORKDIR"/pkg/rootfs.itbh
|
||||
|
||||
signsize=$(printf '0x%x' $(fdtget "$itbh" /images/rootfs data-position))
|
||||
|
||||
dtc -I dtb -O dts "$itbh" | awk -v sq="$sq" '
|
||||
/timestamp =/ || /data-\w+ =/ || /signer-\w+ =/ || /value =/{
|
||||
/* Remove old signature */
|
||||
next;
|
||||
}
|
||||
|
||||
/compression = "none"/ {
|
||||
/* Splice in reference to the squash */
|
||||
print;
|
||||
sub("compression", "data");
|
||||
sub("\"none\"", sprintf("/incbin/(\"%s\")", sq));
|
||||
print;
|
||||
next;
|
||||
}
|
||||
|
||||
{
|
||||
/* Keep the rest as-is */
|
||||
print;
|
||||
};
|
||||
' >"$WORKDIR"/resign.its
|
||||
|
||||
sqsign "$WORKDIR"/resign.its "$keys" "$signsize" "$itbh"
|
||||
}
|
||||
|
||||
unpack()
|
||||
{
|
||||
local pkg="$1"
|
||||
local keys="$2"
|
||||
WORKDIR=$(readlink -f "$3")
|
||||
|
||||
case "$#" in
|
||||
3)
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ixbin unpack PKG KEYS DIR" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
local crt=$(ls "$keys"/*.crt | head -n1)
|
||||
[ -r "$crt" ] || die "No public key found in $keys"
|
||||
|
||||
msg "Setting up working directory"
|
||||
mkdir -p "$WORKDIR"
|
||||
cp -a "$keys" "$WORKDIR"/in-keys
|
||||
|
||||
touch "$WORKDIR"/fakeroot-state
|
||||
FAKEROOT="fakeroot -i $WORKDIR/fakeroot-state -s $WORKDIR/fakeroot-state"
|
||||
|
||||
msg "Unpacking RAUC bundle"
|
||||
rauc --keyring="$crt" extract "$pkg" "$WORKDIR"/pkg
|
||||
msg "Unpacking Squash filesystem"
|
||||
squnpack
|
||||
msg "OK, $WORKDIR/rootfs can now be modified"
|
||||
}
|
||||
|
||||
run()
|
||||
{
|
||||
WORKDIR=$(readlink -f "$1")
|
||||
shift
|
||||
|
||||
[ -r "$WORKDIR"/fakeroot-state ] || \
|
||||
die "$WORKDIR does not contain an unpacked image"
|
||||
FAKEROOT="fakeroot -i $WORKDIR/fakeroot-state -s $WORKDIR/fakeroot-state"
|
||||
|
||||
sqexec "$@"
|
||||
}
|
||||
|
||||
pack()
|
||||
{
|
||||
WORKDIR=$(readlink -f "$1")
|
||||
local keys="$2"
|
||||
local pkg="$3"
|
||||
|
||||
case "$#" in
|
||||
3)
|
||||
;;
|
||||
2)
|
||||
keys="$WORKDIR"/in-keys
|
||||
pkg="$2"
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ixbin pack DIR [KEYS] DIR" >&2
|
||||
exit 1
|
||||
esac
|
||||
|
||||
[ -r "$WORKDIR"/fakeroot-state ] || \
|
||||
die "$WORKDIR does not contain an unpacked image"
|
||||
|
||||
local crt=$(ls "$keys"/*.crt | head -n1)
|
||||
[ -r "$crt" ] || die "No public key found in $keys"
|
||||
|
||||
local key=$(ls "$keys"/*.key | head -n1)
|
||||
[ -r "$crt" ] || die "No private key found in $keys"
|
||||
|
||||
FAKEROOT="fakeroot -i $WORKDIR/fakeroot-state -s $WORKDIR/fakeroot-state"
|
||||
|
||||
msg "Packing up Squash filesystem"
|
||||
sqpack
|
||||
[ -f "$WORKDIR"/pkg/rootfs.itbh ] && {
|
||||
msg "Updating Squash filesystem signature"
|
||||
sqresign "$keys"
|
||||
}
|
||||
|
||||
msg "Creating RAUC bundle"
|
||||
rauc --keyring="$crt" --cert="$crt" --key="$key" bundle "$WORKDIR"/pkg "$pkg"
|
||||
msg "OK, updated bundle available in $pkg"
|
||||
}
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cmd="$1"
|
||||
shift
|
||||
|
||||
case "$cmd" in
|
||||
unpack)
|
||||
ensuredeps
|
||||
unpack "$@"
|
||||
;;
|
||||
"exec")
|
||||
ensuredeps
|
||||
run "$@"
|
||||
;;
|
||||
pack)
|
||||
ensuredeps
|
||||
pack "$@"
|
||||
;;
|
||||
help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
die "Unknown command \"$cmd\""
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user