mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-06-03 01:44:53 +00:00
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a6f34820fb | ||
![]() |
75edc8b146 | ||
![]() |
7842ad8059 | ||
![]() |
530e2df7cf | ||
![]() |
7fb6ee0ff7 | ||
![]() |
e3d2a860d0 | ||
![]() |
8aaf0b9cac | ||
![]() |
53c1b6c9ea | ||
![]() |
2b1e5e66fb | ||
![]() |
5ec00e6ec1 | ||
![]() |
a708ffd175 | ||
![]() |
6d1b5c0c9d | ||
![]() |
86f54cf5c3 | ||
![]() |
e8bca1dcf0 | ||
![]() |
24d365e19b | ||
![]() |
62c2b834c6 | ||
![]() |
69e0ef7631 | ||
![]() |
599d53de34 | ||
![]() |
79cd5620c5 | ||
![]() |
30596d666b | ||
![]() |
4368a15d79 | ||
![]() |
3129aa16ea | ||
![]() |
251934559b | ||
![]() |
75a45c762f | ||
![]() |
d1c439c2dc | ||
![]() |
103789cb69 | ||
![]() |
d0f0710c78 | ||
![]() |
5d5a52930f | ||
![]() |
fb3e245554 | ||
![]() |
1a1f738903 | ||
![]() |
685dd0090c | ||
![]() |
cccda67781 | ||
![]() |
5d444a7ab9 | ||
![]() |
19b0348659 | ||
![]() |
db0f21164e | ||
![]() |
af8c24846c | ||
![]() |
e5b98de8b3 | ||
![]() |
e61dc8f00a | ||
![]() |
1182776075 | ||
![]() |
ad825b1308 | ||
![]() |
80d6f064c3 | ||
![]() |
7a39098c13 | ||
![]() |
32d3026be3 | ||
![]() |
97a7806148 | ||
![]() |
1c50dd2584 | ||
![]() |
d18bedd2de | ||
![]() |
b7ecc30b8f | ||
![]() |
c407692c10 | ||
![]() |
c94cd8c685 | ||
![]() |
86d710dd02 | ||
![]() |
9cd047ed88 | ||
![]() |
cf0afb0cce | ||
![]() |
62b6a63f57 | ||
![]() |
b2ced7173f | ||
![]() |
2f50886ba2 | ||
![]() |
c62f26884e | ||
![]() |
2b9a3359db | ||
![]() |
d6a9cd869c | ||
![]() |
cb1bda7e3c | ||
![]() |
bce1cdce45 | ||
![]() |
d83f78047f | ||
![]() |
f0bdaaacce | ||
![]() |
ea9ca53912 | ||
![]() |
8393b4401f | ||
![]() |
d4d1aac9ec | ||
![]() |
cb3d349fc8 | ||
![]() |
dc78072602 | ||
![]() |
96501020b3 | ||
![]() |
4975b9b914 |
2
.github/workflows/dependabot_auto.yml
vendored
2
.github/workflows/dependabot_auto.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v2.3.0
|
||||
uses: dependabot/fetch-metadata@v2.4.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
skip-commit-verification: true
|
||||
|
2
.github/workflows/osv-scanner-pr.yaml
vendored
2
.github/workflows/osv-scanner-pr.yaml
vendored
@ -18,4 +18,4 @@ permissions:
|
||||
|
||||
jobs:
|
||||
scan-pr:
|
||||
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.0.0"
|
||||
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.0.2"
|
||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -14,6 +14,8 @@ jobs:
|
||||
git fetch --prune --unshallow
|
||||
- name: Generate version
|
||||
run: echo "VERSION=$(git describe --always --tags --dirty)" >> $GITHUB_ENV
|
||||
- name: Install gcc for arm64
|
||||
run: sudo apt-get update && sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
|
2
.github/workflows/secscan.yaml
vendored
2
.github/workflows/secscan.yaml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@v2.22.2
|
||||
uses: securego/gosec@v2.22.4
|
||||
with:
|
||||
# we let the report trigger content trigger a failure using the GitHub Security features.
|
||||
args: '-no-fail -fmt sarif -out results.sarif ./...'
|
||||
|
9
.github/workflows/unit-tests.yml
vendored
9
.github/workflows/unit-tests.yml
vendored
@ -11,9 +11,6 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
unit-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ "1.23" ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@ -21,9 +18,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Go environment
|
||||
uses: actions/setup-go@v5.4.0
|
||||
uses: actions/setup-go@v5.5.0
|
||||
with:
|
||||
go-version: '${{ matrix.go-version }}'
|
||||
go-version-file: go.mod
|
||||
- name: Run tests
|
||||
run: |
|
||||
go run github.com/onsi/ginkgo/v2/ginkgo run -p --github-output --covermode=atomic --coverprofile=coverage.out --race -r ./...
|
||||
@ -32,4 +29,4 @@ jobs:
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
with:
|
||||
file: ./coverage.out
|
||||
files: ./coverage.out
|
||||
|
2
.github/workflows/webui.yaml
vendored
2
.github/workflows/webui.yaml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
webui:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ "1.23-bookworm" ]
|
||||
go-version: [ "1.24-bookworm" ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
@ -1,8 +1,8 @@
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
project_name: kairos-agent
|
||||
version: 2
|
||||
builds:
|
||||
- ldflags:
|
||||
- -w -s -X "github.com/kairos-io/kairos-agent/v2/internal/common.VERSION={{.Tag}}"
|
||||
- -w -s -X "github.com/kairos-io/kairos-agent/v2/internal/common.VERSION={{.Tag}}" -X "github.com/kairos-io/kairos-agent/v2/internal/common.gitCommit={{.Commit}}"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
@ -11,17 +11,53 @@ builds:
|
||||
- amd64
|
||||
- arm64
|
||||
binary: '{{ .ProjectName }}'
|
||||
id: default
|
||||
- ldflags:
|
||||
- -w -s -X "github.com/kairos-io/kairos-agent/v2/internal/common.VERSION={{.Tag}}" -X "github.com/kairos-io/kairos-agent/v2/internal/common.gitCommit={{.Commit}}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- GOEXPERIMENT=boringcrypto
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
binary: '{{ .ProjectName }}'
|
||||
id: fips-amd64
|
||||
hooks:
|
||||
post:
|
||||
- bash -c 'set -e; go version {{.Path}} | grep boringcrypto || (echo "boringcrypto not found" && exit 1)'
|
||||
- ldflags:
|
||||
- -w -s -X "github.com/kairos-io/kairos-agent/v2/internal/common.VERSION={{.Tag}}" -X "github.com/kairos-io/kairos-agent/v2/internal/common.gitCommit={{.Commit}}"
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- GOEXPERIMENT=boringcrypto
|
||||
- CC=aarch64-linux-gnu-gcc
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- arm64
|
||||
binary: '{{ .ProjectName }}'
|
||||
id: fips-arm64
|
||||
hooks:
|
||||
post:
|
||||
- bash -c 'set -e; go version {{.Path}} | grep boringcrypto || (echo "boringcrypto not found" && exit 1)'
|
||||
source:
|
||||
enabled: true
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-source'
|
||||
archives:
|
||||
# Default template uses underscores instead of -
|
||||
- name_template: >-
|
||||
{{ .ProjectName }}-{{ .Tag }}-{{- title .Os }}-{{- if eq .Arch "amd64" }}x86_64{{- else if eq .Arch "386" }}i386{{- else }}{{ .Arch }}{{ end }}{{- if .Arm }}v{{ .Arm }}{{ end }}
|
||||
- id: default-archive
|
||||
ids:
|
||||
- default
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
|
||||
- id: fips-archive
|
||||
ids:
|
||||
- fips-arm64
|
||||
- fips-amd64
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}-{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}-fips'
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
version_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
|
81
go.mod
81
go.mod
@ -1,49 +1,47 @@
|
||||
module github.com/kairos-io/kairos-agent/v2
|
||||
|
||||
go 1.23.6
|
||||
|
||||
toolchain go1.24.1
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.3.1
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1
|
||||
github.com/diskfs/go-diskfs v1.4.2
|
||||
github.com/diskfs/go-diskfs v1.6.0
|
||||
github.com/erikgeiser/promptkit v0.9.0
|
||||
github.com/google/go-containerregistry v0.20.3
|
||||
github.com/google/go-containerregistry v0.20.5
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/jaypipes/ghw v0.16.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kairos-io/go-nodepair v0.3.0
|
||||
github.com/kairos-io/kairos-sdk v0.7.3
|
||||
github.com/kairos-io/kcrypt v0.14.1
|
||||
github.com/labstack/echo/v4 v4.13.3
|
||||
github.com/kairos-io/kairos-sdk v0.9.3
|
||||
github.com/labstack/echo/v4 v4.13.4
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5
|
||||
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82
|
||||
github.com/mudler/yip v1.15.0
|
||||
github.com/mudler/yip v1.16.1
|
||||
github.com/nxadm/tail v1.4.11
|
||||
github.com/onsi/ginkgo/v2 v2.23.3
|
||||
github.com/onsi/gomega v1.36.3
|
||||
github.com/onsi/ginkgo/v2 v2.23.4
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/pterm/pterm v0.12.80
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/sanity-io/litter v1.5.8
|
||||
github.com/sirupsen/logrus v1.9.4-0.20241118143825-d1e633264448
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
golang.org/x/net v0.37.0
|
||||
golang.org/x/oauth2 v0.28.0
|
||||
golang.org/x/sys v0.31.0
|
||||
golang.org/x/net v0.40.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/sys v0.33.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/mount-utils v0.32.3
|
||||
k8s.io/mount-utils v0.33.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/foxboron/go-uefi v0.0.0-20250207204325-69fb7dba244f
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/google/go-github/v69 v69.2.0
|
||||
github.com/google/go-github/v70 v70.0.0
|
||||
github.com/google/go-github/v72 v72.0.0
|
||||
github.com/twpayne/go-vfs/v5 v5.0.4
|
||||
github.com/urfave/cli/v2 v2.27.6
|
||||
)
|
||||
|
||||
require (
|
||||
@ -77,16 +75,17 @@ require (
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/denisbrodbeck/machineid v1.0.1 // indirect
|
||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/cli v27.5.0+incompatible // indirect
|
||||
github.com/docker/cli v28.1.1+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v27.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/docker v28.1.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/edsrzf/mmap-go v1.2.0 // indirect
|
||||
@ -94,12 +93,11 @@ require (
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/foxboron/go-uefi v0.0.0-20241219185318-19dc140271bf // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20230802011745-f2460f5984f7 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.13.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.14.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
@ -109,7 +107,7 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 // indirect
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
@ -125,7 +123,7 @@ require (
|
||||
github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237 // indirect
|
||||
github.com/kendru/darwin/go/depgraph v0.0.0-20230809052043-4d1c7e9d1767 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
@ -133,7 +131,7 @@ require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/makiuchi-d/gozxing v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
@ -152,10 +150,7 @@ require (
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/otiai10/copy v1.14.1 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/packethost/packngo v0.29.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
@ -165,7 +160,6 @@ require (
|
||||
github.com/pkg/xattr v0.4.9 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/qeesung/image2ascii v1.0.1 // indirect
|
||||
github.com/rancher-sandbox/linuxkit v1.0.2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/saferwall/pe v1.5.6 // indirect
|
||||
@ -196,9 +190,7 @@ require (
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // indirect
|
||||
github.com/vishvananda/netlink v1.3.0 // indirect
|
||||
github.com/vishvananda/netns v0.0.4 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/vmware/vmw-guestinfo v0.0.0-20220317130741-510905f0efa3 // indirect
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
@ -209,19 +201,20 @@ require (
|
||||
github.com/zcalusic/sysinfo v1.1.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/crypto v0.38.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/image v0.20.0 // indirect
|
||||
golang.org/x/mod v0.23.0 // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/term v0.30.0 // indirect
|
||||
golang.org/x/text v0.23.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/term v0.32.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
|
179
go.sum
179
go.sum
@ -42,8 +42,6 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/anatol/devmapper.go v0.0.0-20230829043248-59ac2b9706ba h1:LJ/tQNki21ep58+YZElkXQVpswENcK66NMNv4JGZf7w=
|
||||
github.com/anatol/devmapper.go v0.0.0-20230829043248-59ac2b9706ba/go.mod h1:yZpXZj/k3rAZDY43DteaEzbnnxiz9OYijJqRcqWMKSw=
|
||||
github.com/anatol/luks.go v0.0.0-20240507052915-92f8bb765f98 h1:SML/05friOcB5ohyBaVC1TZPaMsnLFU7OTcTBhTdygk=
|
||||
github.com/anatol/luks.go v0.0.0-20240507052915-92f8bb765f98/go.mod h1:71hQWy01rC95qOpZ315jMB69d4pI/PU6HnZhpnemx90=
|
||||
github.com/anatol/luks.go v0.0.0-20250316021219-8cd744c3576f h1:4tLJrnm3h3biCFsXHQ9w6DVGwuZXW4KMfiKV/atSYXg=
|
||||
github.com/anatol/luks.go v0.0.0-20250316021219-8cd744c3576f/go.mod h1:kEOnWwULAKOORfFvE4dEkdRZJS7+NMJKxRb/vWvmARk=
|
||||
github.com/anatol/vmtest v0.0.0-20230711210602-87511df0d4bc h1:xMQuzBhj6hXQZufedPQM2OiGX2UcQHSptXtG3+28S8Q=
|
||||
@ -105,9 +103,12 @@ github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRcc
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
@ -122,21 +123,26 @@ github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d h1:CPqTNIigGwe
|
||||
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d/go.mod h1:QX5ZVULjAfZJux/W62Y91HvCh9hyW6enAwcrrv/sLj0=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/diskfs/go-diskfs v1.4.2 h1:khBr9RTkqAZFaMYK7PP8NooL30hqj3bSgRmj3Ouguls=
|
||||
github.com/diskfs/go-diskfs v1.4.2/go.mod h1:ss1uAUBhgDdEOewZFDWWpYqJFjNPbK7hYSjRoQE+D94=
|
||||
github.com/diskfs/go-diskfs v1.6.0 h1:YmK5+vLSfkwC6kKKRTRPGaDGNF+Xh8FXeiNHwryDfu4=
|
||||
github.com/diskfs/go-diskfs v1.6.0/go.mod h1:bRFumZeGFCO8C2KNswrQeuj2m1WCVr4Ms5IjWMczMDk=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
|
||||
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
|
||||
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
|
||||
github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
|
||||
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@ -144,8 +150,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||
github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
|
||||
github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
|
||||
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/eliukblau/pixterm v1.3.2 h1:kAF9qvbaDV3emb9LPHw1Bvd9D5o4y28U0e8Q9vfl24I=
|
||||
github.com/eliukblau/pixterm v1.3.2/go.mod h1:CgaInx2l92Xo3GTldly4UQeNghSFXmIQNk3zL77Xo/A=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||
@ -160,8 +166,8 @@ github.com/erikgeiser/promptkit v0.9.0 h1:3qL1mS/ntCrXdb8sTP/ka82CJ9kEQaGuYXNrYJ
|
||||
github.com/erikgeiser/promptkit v0.9.0/go.mod h1:pU9dtogSe3Jlc2AY77EP7R4WFP/vgD4v+iImC83KsCo=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/foxboron/go-uefi v0.0.0-20241219185318-19dc140271bf h1:eKPYdh9Dq7P/Tc6GRt4HqqsVK8b2vt0IGP+xmZ8dMjo=
|
||||
github.com/foxboron/go-uefi v0.0.0-20241219185318-19dc140271bf/go.mod h1:q85c4IRlhhwdRJgGIUWrisDjU8dgcMj8dnXZCXo3hus=
|
||||
github.com/foxboron/go-uefi v0.0.0-20250207204325-69fb7dba244f h1:SGo7y1xmmGWiQzp7QU3ueehmdMVkjj9Yyo1IDEuHbYw=
|
||||
github.com/foxboron/go-uefi v0.0.0-20250207204325-69fb7dba244f/go.mod h1:q85c4IRlhhwdRJgGIUWrisDjU8dgcMj8dnXZCXo3hus=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
@ -179,8 +185,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
|
||||
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@ -230,13 +236,17 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
github.com/google/go-containerregistry v0.20.4 h1:w/Fdj3ef046SdV/GJU69cCnreaLpqbTo1X9XPyHbkd4=
|
||||
github.com/google/go-containerregistry v0.20.4/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E=
|
||||
github.com/google/go-containerregistry v0.20.5 h1:4RnlYcDs5hoA++CeFjlbZ/U9Yp1EuWr+UhhTyYQjOP0=
|
||||
github.com/google/go-containerregistry v0.20.5/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E=
|
||||
github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE=
|
||||
github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
|
||||
github.com/google/go-github/v70 v70.0.0/go.mod h1:xBUZgo8MI3lUL/hwxl3hlceJW1U8MVnXP3zUyI+rhQY=
|
||||
github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941 h1:43XjGa6toxLpeksjcxs1jIoIyr+vUfOqY2c6HB4bpoc=
|
||||
github.com/google/pprof v0.0.0-20250208200701-d0013a598941/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -266,8 +276,6 @@ github.com/itchyny/gojq v0.12.17 h1:8av8eGduDb5+rvEdaOO+zQUjA04MS0m3Ps8HiD+fceg=
|
||||
github.com/itchyny/gojq v0.12.17/go.mod h1:WBrEMkgAfAGO1LUcGOckBl5O726KPp+OlkKug0I/FEY=
|
||||
github.com/itchyny/timefmt-go v0.1.6 h1:ia3s54iciXDdzWzwaVKXZPbiXzxxnv1SPGFfM/myJ5Q=
|
||||
github.com/itchyny/timefmt-go v0.1.6/go.mod h1:RRDZYC5s9ErkjQvTvvU7keJjxUYzIISJGxm9/mAERQg=
|
||||
github.com/jaypipes/ghw v0.15.0 h1:kjn+8fWVtB/DKfwMwpojLFMM6a3zdBF1OnBhAbvJ1BI=
|
||||
github.com/jaypipes/ghw v0.15.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
|
||||
github.com/jaypipes/ghw v0.16.0 h1:3HurCTS38VNpeQLo5fIdZsySuo/qAfpPSJ5t05QBFPM=
|
||||
github.com/jaypipes/ghw v0.16.0/go.mod h1:In8SsaDqlb1oTyrbmTC14uy+fbBMvp+xdqX51MidlD8=
|
||||
github.com/jaypipes/pcidb v1.0.1 h1:WB2zh27T3nwg8AE8ei81sNRb9yWBii3JGNJtT7K9Oic=
|
||||
@ -283,12 +291,14 @@ github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004 h1:G+9t9cEtnC
|
||||
github.com/jzelinskie/whirlpool v0.0.0-20201016144138-0675e54bb004/go.mod h1:KmHnJWQrgEvbuy0vcvj00gtMqbvNn1L+3YUZLK/B92c=
|
||||
github.com/kairos-io/go-nodepair v0.3.0 h1:JIMBAtbNhIAsx89aP61mQDGMuGFoIQH/woK2tMDYD6k=
|
||||
github.com/kairos-io/go-nodepair v0.3.0/go.mod h1:7i905W/KmR9DAcMSVJr/Wdb84E5Yyu9YLgj7chwX1xs=
|
||||
github.com/kairos-io/kairos-sdk v0.7.3 h1:OyDSEQVtc1MnRrP3M8d+wref0RA3eZof/FTL5ETOlXM=
|
||||
github.com/kairos-io/kairos-sdk v0.7.3/go.mod h1:ZSxP3VgOE2+f/3IdPNcGK7qcYLWl44zV+gq0m+9ovoo=
|
||||
github.com/kairos-io/kcrypt v0.14.0 h1:7dEg/gLDJkT06XutnLFwjp8ExfxtgYImF8OaCoVRbpY=
|
||||
github.com/kairos-io/kcrypt v0.14.0/go.mod h1:qz/QBCg7phprxHogsjouhlish2Jz92JsmFbJVg2M5cI=
|
||||
github.com/kairos-io/kcrypt v0.14.1 h1:dkZ+dJO9reAlZ60R/aS5iDyg/ZC3MtWTPUEfiYvSPyE=
|
||||
github.com/kairos-io/kcrypt v0.14.1/go.mod h1:Bn0a2d09xrRvL/n5CTtqsCFKu900vtOpdgxAhDRJJu8=
|
||||
github.com/kairos-io/kairos-sdk v0.9.0 h1:Bcpf3nUwGvzreIdXBIZZRnS2LDPs496C0Reo+dpbkMs=
|
||||
github.com/kairos-io/kairos-sdk v0.9.0/go.mod h1:O3si3aCkYsOyjjLF2jKKTKUYW9948WcB7xR0ivKbB6M=
|
||||
github.com/kairos-io/kairos-sdk v0.9.1 h1:5MagNf3ghNsQaH6sVXXLVSjClrDQ9UZrxjHYRghk26Q=
|
||||
github.com/kairos-io/kairos-sdk v0.9.1/go.mod h1:O3si3aCkYsOyjjLF2jKKTKUYW9948WcB7xR0ivKbB6M=
|
||||
github.com/kairos-io/kairos-sdk v0.9.2 h1:A/9rbRpjZsBWniXSPzvT7I2dbbukgveUjrvk9iXH4AE=
|
||||
github.com/kairos-io/kairos-sdk v0.9.2/go.mod h1:O3si3aCkYsOyjjLF2jKKTKUYW9948WcB7xR0ivKbB6M=
|
||||
github.com/kairos-io/kairos-sdk v0.9.3 h1:je3Q0mfm1p4y3jO3k0P/SUp4NEax8IwLveDlnZBB8Yc=
|
||||
github.com/kairos-io/kairos-sdk v0.9.3/go.mod h1:O3si3aCkYsOyjjLF2jKKTKUYW9948WcB7xR0ivKbB6M=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kbinani/screenshot v0.0.0-20230812210009-b87d31814237 h1:YOp8St+CM/AQ9Vp4XYm4272E77MptJDHkwypQHIRl9Q=
|
||||
@ -302,6 +312,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
@ -316,6 +328,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY=
|
||||
github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g=
|
||||
github.com/labstack/echo/v4 v4.13.4 h1:oTZZW+T3s9gAu5L8vmzihV7/lkXGZuITzTQkTEhcXEA=
|
||||
github.com/labstack/echo/v4 v4.13.4/go.mod h1:g63b33BZ5vZzcIUF8AtRH40DrTlXnx4UMC8rBdndmjQ=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
@ -332,6 +346,8 @@ github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9
|
||||
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
@ -370,8 +386,10 @@ github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5 h1:FaZD86+A9mV
|
||||
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5/go.mod h1:WmKcT8ONmhDQIqQ+HxU+tkGWjzBEyY/KFO8LTGCu4AI=
|
||||
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82 h1:FVT07EI8njvsD4tC2Hw8Xhactp5AWhsQWD4oTeQuSAU=
|
||||
github.com/mudler/go-processmanager v0.0.0-20240820160718-8b802d3ecf82/go.mod h1:Urp7LG5jylKoDq0663qeBh0pINGcRl35nXdKx82PSoU=
|
||||
github.com/mudler/yip v1.15.0 h1:msYgf+ZGFgresmdo9xB6+eVj6EnpRqelTU2ph1qKCs8=
|
||||
github.com/mudler/yip v1.15.0/go.mod h1:xBWq88HvpJ5aJGJy4fCuhJ6lePG+uiE4z2RKstqO3n8=
|
||||
github.com/mudler/yip v1.16.0 h1:TZr9zLghe5CJXRdvBK6f5uHe6RJtotweDU+m/GNT+gY=
|
||||
github.com/mudler/yip v1.16.0/go.mod h1:Wk3CIZCqdK58+1CzamA87atJD2y/dhDKXrguUyYipCc=
|
||||
github.com/mudler/yip v1.16.1 h1:SUq136jJ6QnX0FgP87IoIvkLT8OdEp3DYQUlKzI/gOQ=
|
||||
github.com/mudler/yip v1.16.1/go.mod h1:Wk3CIZCqdK58+1CzamA87atJD2y/dhDKXrguUyYipCc=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
@ -390,31 +408,19 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
|
||||
github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.1 h1:Ox0cOPv/t8RzKJUfDo9ZKtRvBOJY369sFJnl00CjqwY=
|
||||
github.com/onsi/ginkgo/v2 v2.23.1/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.2 h1:LYLd7Wz401p0N7xR8y7WL6D2QZwKpbirDg0EVIvzvMM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.2/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
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.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/packethost/packngo v0.29.0 h1:gRIhciVZQ/zLNrIdIdbOUyB/Tw5IgoaXyhP4bvE+D2s=
|
||||
github.com/packethost/packngo v0.29.0/go.mod h1:/UHguFdPs6Lf6FOkkSEPnRY5tgS0fsVM+Zv/bvBrmt0=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/phayes/permbits v0.0.0-20190612203442-39d7c581d2ee h1:P6U24L02WMfj9ymZTxl7CxS73JC99x3ukk+DBkgQGQs=
|
||||
@ -436,6 +442,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
|
||||
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
|
||||
@ -448,19 +456,14 @@ github.com/pterm/pterm v0.12.80 h1:mM55B+GnKUnLMUSqhdINe4s6tOuVQIetQ3my8JGyAIg=
|
||||
github.com/pterm/pterm v0.12.80/go.mod h1:c6DeF9bSnOSeFPZlfs4ZRAFcf5SCoTwvwQ5xaKGQlHo=
|
||||
github.com/qeesung/image2ascii v1.0.1 h1:Fe5zTnX/v/qNC3OC4P/cfASOXS501Xyw2UUcgrLgtp4=
|
||||
github.com/qeesung/image2ascii v1.0.1/go.mod h1:kZKhyX0h2g/YXa/zdJR3JnLnJ8avHjZ3LrvEKSYyAyU=
|
||||
github.com/rancher-sandbox/linuxkit v1.0.2 h1:mUFPL2Mgl1XZ5H82ABR57t5H2G2Qd+lu3gMYvUGmeZo=
|
||||
github.com/rancher-sandbox/linuxkit v1.0.2/go.mod h1:n6Fkjc5qoMeWrnLSA5oqUF8ZzFKMrM960CtBwfvH1ZM=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
@ -519,7 +522,6 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -559,10 +561,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
|
||||
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
|
||||
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
|
||||
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vmware/vmw-guestinfo v0.0.0-20220317130741-510905f0efa3 h1:v6jG/tdl4O07LNVp74Nt7/OyL+1JsIW1M2f/nSvQheY=
|
||||
github.com/vmware/vmw-guestinfo v0.0.0-20220317130741-510905f0efa3/go.mod h1:CSBTxrhePCm0cmXNKDGeu+6bOQzpaEklfCqEpn89JWk=
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0 h1:EB7cIzBdsOzAgmhTUtTTQXBByuPheP/Zv1zL2BRPY6g=
|
||||
@ -595,30 +595,41 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg=
|
||||
go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
@ -632,8 +643,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -650,11 +661,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
|
||||
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -662,8 +677,10 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -694,23 +711,25 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
|
||||
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -718,8 +737,10 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -732,8 +753,10 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
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=
|
||||
@ -797,8 +820,10 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/mount-utils v0.32.3 h1:ZPXXHblfBhYP89OnaozpFg9Ojl6HhDfxBLcdWNkaxW8=
|
||||
k8s.io/mount-utils v0.32.3/go.mod h1:Kun5c2svjAPx0nnvJKYQWhfeNW+O0EpzHgRhDcYoSY0=
|
||||
k8s.io/mount-utils v0.33.0 h1:hH6EcCcax4lFNIERaGMj6d7oGMW1qW3eTCwHUuLtLog=
|
||||
k8s.io/mount-utils v0.33.0/go.mod h1:1JR4rKymg8B8bCPo618hpSAdrpO6XLh0Acqok/xVwPE=
|
||||
k8s.io/mount-utils v0.33.1 h1:hodPhfyoK+gG0SgnYwx1iPrlnpaESZiJ9GFzF5V/imE=
|
||||
k8s.io/mount-utils v0.33.1/go.mod h1:1JR4rKymg8B8bCPo618hpSAdrpO6XLh0Acqok/xVwPE=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
pault.ag/go/modprobe v0.2.0 h1:VF04w/Lez7qFHZX9QUZo2jVyTZINvYZG9dfvgZVXwXU=
|
||||
|
@ -1,7 +1,6 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@ -43,7 +42,7 @@ func (b BundlePostInstall) Run(c config.Config, _ v1.Spec) error {
|
||||
syscall.Sync()
|
||||
err := c.Syscall.Mount(filepath.Join("/dev/disk/by-label", constants.PersistentLabel), constants.UsrLocalPath, "ext4", 0, "")
|
||||
if err != nil {
|
||||
fmt.Printf("could not mount persistent: %s\n", err)
|
||||
c.Logger.Logger.Err(err).Msg("could not mount persistent")
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
internalutils "github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
"github.com/kairos-io/kairos-sdk/kcrypt"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
kcrypt "github.com/kairos-io/kcrypt/pkg/lib"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -43,7 +43,11 @@ func (k Finish) Run(c config.Config, spec v1.Spec) error {
|
||||
c.Logger.Logger.Info().Msg("Finished encrypt hook")
|
||||
}
|
||||
|
||||
// Now that we have everything encrypted and ready if needed
|
||||
// Now that we have everything encrypted and ready to mount if needed
|
||||
err = GrubPostInstallOptions{}.Run(c, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = BundlePostInstall{}.Run(c, spec)
|
||||
if err != nil {
|
||||
c.Logger.Logger.Warn().Err(err).Msg("could not copy run bundles post install")
|
||||
@ -79,14 +83,14 @@ func Encrypt(c config.Config, _ v1.Spec) error {
|
||||
}()
|
||||
|
||||
for _, p := range c.Install.Encrypt {
|
||||
_, err := kcrypt.Luksify(p, c.Logger)
|
||||
_, err := kcrypt.Encrypt(p, c.Logger)
|
||||
if err != nil {
|
||||
c.Logger.Errorf("could not encrypt partition: %s", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_ = kcrypt.UnlockAllWithLogger(false, c.Logger)
|
||||
_ = kcrypt.UnlockAll(false, c.Logger)
|
||||
|
||||
for _, p := range c.Install.Encrypt {
|
||||
for i := 0; i < 10; i++ {
|
||||
@ -98,7 +102,7 @@ func Encrypt(c config.Config, _ v1.Spec) error {
|
||||
time.Sleep(time.Duration(i) * time.Second)
|
||||
// Retry the unlock as well, because maybe the partition was not refreshed on time for unlock to unlock it
|
||||
// So no matter how many tries we do, it will still be locked and will never appear
|
||||
err := kcrypt.UnlockAllWithLogger(false, c.Logger)
|
||||
err := kcrypt.UnlockAll(false, c.Logger)
|
||||
if err != nil {
|
||||
c.Logger.Debugf("UnlockAll returned: %s", err)
|
||||
}
|
||||
@ -188,7 +192,7 @@ func EncryptUKI(c config.Config, spec v1.Spec) error {
|
||||
for _, p := range append([]string{constants.OEMLabel, constants.PersistentLabel}, c.Install.Encrypt...) {
|
||||
c.Logger.Infof("Encrypting %s", p)
|
||||
_ = os.Setenv("SYSTEMD_LOG_LEVEL", "debug")
|
||||
err = kcrypt.LuksifyMeasurements(p, c.BindPublicPCRs, c.BindPCRs, c.Logger)
|
||||
err = kcrypt.EncryptWithPcrs(p, c.BindPublicPCRs, c.BindPCRs, c.Logger)
|
||||
_ = os.Unsetenv("SYSTEMD_LOG_LEVEL")
|
||||
if err != nil {
|
||||
c.Logger.Errorf("could not encrypt partition: %s", err)
|
||||
@ -201,7 +205,7 @@ func EncryptUKI(c config.Config, spec v1.Spec) error {
|
||||
|
||||
_ = os.Setenv("SYSTEMD_LOG_LEVEL", "debug")
|
||||
|
||||
err = kcrypt.UnlockAllWithLogger(true, c.Logger)
|
||||
err = kcrypt.UnlockAll(true, c.Logger)
|
||||
|
||||
_ = os.Unsetenv("SYSTEMD_LOG_LEVEL")
|
||||
if err != nil {
|
||||
@ -223,7 +227,7 @@ func EncryptUKI(c config.Config, spec v1.Spec) error {
|
||||
time.Sleep(time.Duration(i) * time.Second)
|
||||
// Retry the unlock as well, because maybe the partition was not refreshed on time for unlock to unlock it
|
||||
// So no matter how many tries we do, it will still be locked and will never appear
|
||||
err := kcrypt.UnlockAllWithLogger(true, c.Logger)
|
||||
err := kcrypt.UnlockAll(true, c.Logger)
|
||||
if err != nil {
|
||||
c.Logger.Debugf("UnlockAll returned: %s", err)
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"strings"
|
||||
|
||||
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos-sdk/system"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/state"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type GrubOptions struct{}
|
||||
@ -16,9 +18,9 @@ func (b GrubOptions) Run(c config.Config, _ v1.Spec) error {
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Running GrubOptions hook")
|
||||
c.Logger.Debugf("Setting grub options: %s", c.Install.GrubOptions)
|
||||
err := system.Apply(system.SetGRUBOptions(c.Install.GrubOptions))
|
||||
if err != nil && !strings.Contains(err.Error(), "0 errors occurred") {
|
||||
c.Logger.Logger.Error().Err(err).Msg("Failed to set grub options")
|
||||
err := grubOptions(c, c.Install.GrubOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Finish GrubOptions hook")
|
||||
return nil
|
||||
@ -31,10 +33,32 @@ func (b GrubPostInstallOptions) Run(c config.Config, _ v1.Spec) error {
|
||||
return nil
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Running GrubOptions hook")
|
||||
err := system.Apply(system.SetGRUBOptions(c.GrubOptions))
|
||||
c.Logger.Debugf("Setting grub options: %s", c.GrubOptions)
|
||||
err := grubOptions(c, c.GrubOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Finish GrubOptions hook")
|
||||
return nil
|
||||
}
|
||||
|
||||
// grubOptions sets the grub options in the grubenv file
|
||||
// It mounts the OEM partition if not already mounted
|
||||
// If its mounted but RO, it remounts it as RW
|
||||
func grubOptions(c config.Config, opts map[string]string) error {
|
||||
runtime, err := state.NewRuntime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !runtime.OEM.Mounted {
|
||||
err = machine.Mount(cnst.OEMLabel, cnst.OEMPath)
|
||||
defer func() {
|
||||
_ = machine.Umount(cnst.OEMPath)
|
||||
}()
|
||||
}
|
||||
err = utils.SetPersistentVariables(filepath.Join(runtime.OEM.MountPoint, "grubenv"), opts, &c)
|
||||
if err != nil {
|
||||
c.Logger.Logger.Error().Err(err).Msg("Failed to set grub options")
|
||||
}
|
||||
c.Logger.Logger.Debug().Msg("Running GrubOptions hook")
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"strings"
|
||||
@ -15,8 +15,7 @@ type Interface interface {
|
||||
// FinishInstall is a list of hooks that run when the install process is finished completely.
|
||||
// Its mean for options that are not related to the install process itself
|
||||
var FinishInstall = []Interface{
|
||||
&GrubOptions{}, // Set custom GRUB options in OEM partition
|
||||
&Lifecycle{}, // Handles poweroff/reboot by config options
|
||||
&Lifecycle{}, // Handles poweroff/reboot by config options
|
||||
}
|
||||
|
||||
// FinishReset is a list of hooks that run when the reset process is finished completely.
|
||||
@ -46,7 +45,7 @@ var FinishUKIInstall = []Interface{
|
||||
// PostInstall is a list of hooks that run after the install process has run.
|
||||
// Runs things that need to be done before we run other post install stages like
|
||||
// encrypting partitions, copying the install logs or installing bundles
|
||||
// Most of this options are optional so they are not run by default unless specified int he config
|
||||
// Most of this options are optional so they are not run by default unless specified in the config
|
||||
var PostInstall = []Interface{
|
||||
&Finish{},
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
@ -39,7 +38,7 @@ func (k CopyLogs) Run(c config.Config, _ v1.Spec) error {
|
||||
_ = utils.MkdirAll(c.Fs, constants.PersistentDir, 0755)
|
||||
err := c.Syscall.Mount(filepath.Join("/dev/disk/by-label", constants.PersistentLabel), constants.PersistentDir, "ext4", 0, "")
|
||||
if err != nil {
|
||||
fmt.Printf("could not mount persistent: %s\n", err)
|
||||
c.Logger.Logger.Warn().Err(err).Msg("could not mount persistent")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ func Recovery() error {
|
||||
}
|
||||
|
||||
if busErr != "" {
|
||||
return fmt.Errorf(busErr)
|
||||
return fmt.Errorf("%s", busErr)
|
||||
}
|
||||
|
||||
if !agentConfig.Fast {
|
||||
|
98
internal/webui/public/package-lock.json
generated
98
internal/webui/public/package-lock.json
generated
@ -20,21 +20,10 @@
|
||||
"cypress": "^14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@colors/colors": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/@cypress/request": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz",
|
||||
"integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz",
|
||||
"integrity": "sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@ -51,7 +40,7 @@
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "6.13.1",
|
||||
"qs": "6.14.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
@ -380,9 +369,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/bootstrap": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
|
||||
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
|
||||
"version": "5.3.6",
|
||||
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
|
||||
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
@ -454,9 +443,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||
"integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -468,14 +457,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
|
||||
"integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"get-intrinsic": "^1.2.6"
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"get-intrinsic": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@ -571,9 +560,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cli-table3": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz",
|
||||
"integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.1.tgz",
|
||||
"integrity": "sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -583,7 +572,7 @@
|
||||
"node": "10.* || >= 12.*"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@colors/colors": "1.5.0"
|
||||
"colors": "1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-truncate": {
|
||||
@ -636,6 +625,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colors": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
|
||||
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
}
|
||||
},
|
||||
"node_modules/combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
@ -698,14 +698,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cypress": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-14.2.0.tgz",
|
||||
"integrity": "sha512-u7fuc9JEpSYLOdu8mzZDZ/JWsHUzR5pc8i1TeSqMz/bafXp+6IweMAeyphsEJ6/13qbB6nwTEY1m+GUAp6GqCQ==",
|
||||
"version": "14.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cypress/-/cypress-14.4.0.tgz",
|
||||
"integrity": "sha512-/I59Fqxo7fqdiDi3IM2QKA65gZ7+PVejXg404/I8ZSq+NOnrmw+2pnMUJzpoNyg7KABcEBmgpkfAqhV98p7wJA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@cypress/request": "^3.0.7",
|
||||
"@cypress/request": "^3.0.8",
|
||||
"@cypress/xvfb": "^1.2.4",
|
||||
"@types/sinonjs__fake-timers": "8.1.1",
|
||||
"@types/sizzle": "^2.3.2",
|
||||
@ -718,7 +718,7 @@
|
||||
"check-more-types": "^2.24.0",
|
||||
"ci-info": "^4.1.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-table3": "~0.6.5",
|
||||
"cli-table3": "0.6.1",
|
||||
"commander": "^6.2.1",
|
||||
"common-tags": "^1.8.0",
|
||||
"dayjs": "^1.10.4",
|
||||
@ -742,7 +742,7 @@
|
||||
"process": "^0.11.10",
|
||||
"proxy-from-env": "1.0.0",
|
||||
"request-progress": "^3.0.0",
|
||||
"semver": "^7.5.3",
|
||||
"semver": "^7.7.1",
|
||||
"supports-color": "^8.1.1",
|
||||
"tmp": "~0.2.3",
|
||||
"tree-kill": "1.2.2",
|
||||
@ -1070,18 +1070,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||
"integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.0.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.0",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
@ -1819,13 +1819,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz",
|
||||
"integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==",
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@ -1904,9 +1904,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"version": "7.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
|
||||
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
|
317
main.go
317
main.go
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
@ -74,12 +75,13 @@ var cmds = []*cli.Command{
|
||||
&cli.BoolFlag{Name: "recovery", Usage: "Upgrade recovery"},
|
||||
},
|
||||
Description: `
|
||||
Manually upgrade a kairos node Active image. Does not upgrade passive or recovery images.
|
||||
Manually upgrade a kairos node Active image. Does not upgrade the passive image. It upgrades the recovery image when the --recovery flag is passed.
|
||||
|
||||
With no arguments, it defaults to latest available release. To specify a version, pass it as argument using the --source flag.
|
||||
Passing just the Kairos version as the first argument is no longer supported. If you speficy a positional argument, it will be treated
|
||||
To specify a version, pass it as argument using the --source flag. Passing just the Kairos version as the first argument is no longer supported. If you speficy a positional argument, it will be treated
|
||||
as a value for the --source flag.
|
||||
|
||||
You can also specify the upgrade image by setting "upgrade.system.uri" for the active image or "upgrade.recovery-system.uri" for the recovery image, in the cloud config.
|
||||
|
||||
To retrieve all the available versions, use "kairos upgrade list-releases"
|
||||
|
||||
$ kairos upgrade list-releases
|
||||
@ -166,24 +168,21 @@ See https://kairos.io/docs/upgrade/manual/ for documentation.
|
||||
return checkRoot()
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
var v string
|
||||
var source string
|
||||
if c.Args().Len() == 1 {
|
||||
v = c.Args().First()
|
||||
fmt.Println("Warning: Passing a version as a positional argument is deprecated. Use --source flag instead.")
|
||||
fmt.Println("The value will be used as a value for the --source flag")
|
||||
source = c.Args().First()
|
||||
}
|
||||
|
||||
if v := c.String("source"); v != "" {
|
||||
source = v
|
||||
}
|
||||
|
||||
image := c.String("image")
|
||||
if v := c.String("source"); v != "" {
|
||||
source = c.String("source")
|
||||
}
|
||||
|
||||
if image != "" {
|
||||
if v := c.String("image"); v != "" {
|
||||
fmt.Println("--image flag is deprecated, please use --source")
|
||||
// override source with image for now until we drop it
|
||||
source = fmt.Sprintf("oci:%s", image)
|
||||
source = fmt.Sprintf("oci:%s", v)
|
||||
}
|
||||
|
||||
if c.Bool("recovery") && c.String("boot-entry") != "" {
|
||||
@ -805,6 +804,261 @@ The validate command expects a configuration file as its only argument. Local fi
|
||||
return action.ListBootEntries(cfg)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "sysext",
|
||||
Usage: "sysext subcommands",
|
||||
Description: "sysext subcommands",
|
||||
Before: func(c *cli.Context) error {
|
||||
_, err := exec.LookPath("systemd-sysext")
|
||||
if err != nil {
|
||||
return fmt.Errorf("systemd-sysext not found in PATH")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "list",
|
||||
Usage: "List all the installed system extensions",
|
||||
Description: "List all the installed system extensions",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "List the system extensions for the active boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "passive",
|
||||
Usage: "List the system extensions for the passive boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "recovery",
|
||||
Usage: "List the system extensions for the recovery boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "common",
|
||||
Usage: "List the system extensions for the common boot entry (applies to all boot states)",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
if moreThanOneEnabled(c.Bool("active"), c.Bool("passive"), c.Bool("recovery"), c.Bool("common")) {
|
||||
return fmt.Errorf("only one of --active, --passive, --recovery or --common can be set")
|
||||
}
|
||||
|
||||
if err := checkRoot(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
cfg, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bootState string
|
||||
|
||||
if c.Bool("active") {
|
||||
bootState = "active"
|
||||
}
|
||||
if c.Bool("passive") {
|
||||
bootState = "passive"
|
||||
}
|
||||
out, err := action.ListSystemExtensions(cfg, bootState)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(out) == 0 {
|
||||
cfg.Logger.Logger.Info().Msg("No system extensions found")
|
||||
return nil
|
||||
}
|
||||
for _, ext := range out {
|
||||
cfg.Logger.Info(litter.Sdump(ext))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "enable",
|
||||
Usage: "Enable a installed system extension for a give entry",
|
||||
UsageText: "enable [--active|--passive] EXTENSION",
|
||||
Description: "Enable a system extension for a given boot entry (active or passive)",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "Enable the system extension for the active boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "passive",
|
||||
Usage: "Enable the system extension for the passive boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "recovery",
|
||||
Usage: "List the system extensions for the recovery boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "common",
|
||||
Usage: "List the system extensions for the common boot entry (applies to all boot states)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "now",
|
||||
Usage: "Enable the system extension now and reload systemd-sysext",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return fmt.Errorf("extension name required")
|
||||
}
|
||||
|
||||
if moreThanOneEnabled(c.Bool("active"), c.Bool("passive"), c.Bool("recovery"), c.Bool("common")) {
|
||||
return fmt.Errorf("only one of --active, --passive, --recovery or --common can be set")
|
||||
}
|
||||
|
||||
if noneOfEnabled(c.Bool("active"), c.Bool("passive"), c.Bool("recovery"), c.Bool("common")) {
|
||||
return fmt.Errorf("either --active, --passive, --recovery or --common must be set")
|
||||
}
|
||||
|
||||
if err := checkRoot(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
cfg, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bootState string
|
||||
if c.Bool("active") {
|
||||
bootState = "active"
|
||||
}
|
||||
if c.Bool("passive") {
|
||||
bootState = "passive"
|
||||
}
|
||||
ext := c.Args().First()
|
||||
if err := action.EnableSystemExtension(cfg, ext, bootState, c.Bool("now")); err != nil {
|
||||
cfg.Logger.Logger.Error().Err(err).Msg("failed enabling system extension")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "disable",
|
||||
Usage: "Disable a installed system extension for a give entry",
|
||||
UsageText: "disable [--active|--passive] EXTENSION",
|
||||
Description: "Disable a system extension for a given boot entry (active or passive)",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "active",
|
||||
Usage: "Disable the system extension for the active boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "passive",
|
||||
Usage: "Disable the system extension for the passive boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "recovery",
|
||||
Usage: "List the system extensions for the recovery boot entry",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "common",
|
||||
Usage: "List the system extensions for the common boot entry (applies to all boot states)",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "now",
|
||||
Usage: "Disable the system extension now and reload systemd-sysext",
|
||||
},
|
||||
},
|
||||
Before: func(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return fmt.Errorf("extension name required")
|
||||
}
|
||||
|
||||
if moreThanOneEnabled(c.Bool("active"), c.Bool("passive"), c.Bool("recovery"), c.Bool("common")) {
|
||||
return fmt.Errorf("only one of --active, --passive, --recovery or --common can be set")
|
||||
}
|
||||
|
||||
if noneOfEnabled(c.Bool("active"), c.Bool("passive"), c.Bool("recovery"), c.Bool("common")) {
|
||||
return fmt.Errorf("either --active, --passive, --recovery or --common must be set")
|
||||
}
|
||||
if err := checkRoot(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
cfg, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var bootState string
|
||||
if c.Bool("active") {
|
||||
bootState = "active"
|
||||
}
|
||||
if c.Bool("passive") {
|
||||
bootState = "passive"
|
||||
}
|
||||
ext := c.Args().First()
|
||||
if err := action.DisableSystemExtension(cfg, ext, bootState, c.Bool("now")); err != nil {
|
||||
cfg.Logger.Logger.Error().Err(err).Msg("failed disabling system extension")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install a system extension",
|
||||
UsageText: "install URI",
|
||||
Description: "Install a system extension from a given URI",
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return fmt.Errorf("extension URI required")
|
||||
}
|
||||
uri := c.Args().First()
|
||||
if err := validateSourceSysext(uri); err != nil {
|
||||
return err
|
||||
}
|
||||
cfg, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := action.InstallSystemExtension(cfg, uri); err != nil {
|
||||
cfg.Logger.Logger.Error().Err(err).Msg("failed installing system extension")
|
||||
return err
|
||||
}
|
||||
cfg.Logger.Logger.Info().Msgf("System extension %s installed", uri)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Usage: "Remove a system extension",
|
||||
UsageText: "remove EXTENSION",
|
||||
Description: "Remove a installed system extension",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "now",
|
||||
Usage: "Remove the system extension now and reload systemd-sysext",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return fmt.Errorf("extension required")
|
||||
}
|
||||
extension := c.Args().First()
|
||||
cfg, err := agentConfig.Scan(collector.Directories(constants.GetUserConfigDirs()...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := action.RemoveSystemExtension(cfg, extension, c.Bool("now")); err != nil {
|
||||
cfg.Logger.Logger.Error().Err(err).Msg("failed removing system extension")
|
||||
return err
|
||||
}
|
||||
cfg.Logger.Logger.Info().Msgf("System extension %s removed", extension)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
@ -896,6 +1150,22 @@ func validateSource(source string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSourceSysext(source string) error {
|
||||
if source == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
r, err := regexp.Compile(`^oci:|^file:|^http:|^https:`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !r.MatchString(source) {
|
||||
return fmt.Errorf("source %s does not match any of oci:, file: or http(s): ", source)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check
|
||||
func bootFromLiveMedia() bool {
|
||||
// Check if the system is booted from a LIVE media by checking if the file /run/cos/livecd is present
|
||||
@ -929,3 +1199,26 @@ func getReleasesFromProvider(includePrereleases bool) ([]string, error) {
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func moreThanOneEnabled(bools ...bool) bool {
|
||||
count := 0
|
||||
for _, b := range bools {
|
||||
if b {
|
||||
count++
|
||||
}
|
||||
if count > 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func noneOfEnabled(bools ...bool) bool {
|
||||
count := 0
|
||||
for _, b := range bools {
|
||||
if b {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count == 0
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func selectBootEntryGrub(cfg *config.Config, entry string) error {
|
||||
vars := map[string]string{
|
||||
"next_entry": entry,
|
||||
}
|
||||
err = utils.SetPersistentVariables("/oem/grubenv", vars, cfg.Fs)
|
||||
err = utils.SetPersistentVariables("/oem/grubenv", vars, cfg)
|
||||
if err != nil {
|
||||
cfg.Logger.Errorf("could not set default boot entry: %s\n", err)
|
||||
return err
|
||||
|
@ -694,7 +694,7 @@ var _ = Describe("Bootentries tests", Label("bootentry"), func() {
|
||||
err = SelectBootEntry(config, "kairos")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(memLog.String()).To(ContainSubstring("Default boot entry set to kairos"))
|
||||
variables, err := utils.ReadPersistentVariables("/oem/grubenv", fs)
|
||||
variables, err := utils.ReadPersistentVariables("/oem/grubenv", config)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(variables["next_entry"]).To(Equal("kairos"))
|
||||
})
|
||||
|
@ -19,17 +19,15 @@ package action_test
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/diskfs/go-diskfs"
|
||||
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
fileBackend "github.com/diskfs/go-diskfs/backend/file"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/action"
|
||||
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
|
||||
"github.com/kairos-io/kairos-sdk/collector"
|
||||
@ -105,7 +103,8 @@ var _ = Describe("Install action tests", func() {
|
||||
device = filepath.Join(tmpdir, "test.img")
|
||||
Expect(os.RemoveAll(device)).Should(Succeed())
|
||||
// at least 2Gb in size as state is set to 1G
|
||||
_, err = diskfs.Create(device, 2*1024*1024*1024, diskfs.Raw, 512)
|
||||
|
||||
_, err = fileBackend.CreateFromPath(device, 2*1024*1024*1024)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
config.Install.Device = device
|
||||
@ -150,6 +149,16 @@ var _ = Describe("Install action tests", func() {
|
||||
Expect(err).To(BeNil())
|
||||
_, err = fs.Create(grubCfg)
|
||||
Expect(err).To(BeNil())
|
||||
// Create fake grub dir in rootfs and fake grub binaries
|
||||
err = fsutils.MkdirAll(fs, filepath.Join(spec.Active.MountPoint, "sbin"), constants.DirPerm)
|
||||
Expect(err).To(BeNil())
|
||||
f, err := fs.Create(filepath.Join(spec.Active.MountPoint, "sbin", "grub2-install"))
|
||||
Expect(err).To(BeNil())
|
||||
Expect(f.Chmod(0755)).ToNot(HaveOccurred())
|
||||
err = fsutils.MkdirAll(fs, filepath.Join(spec.Active.MountPoint, "usr", "lib", "grub", "i386-pc"), constants.DirPerm)
|
||||
Expect(err).To(BeNil())
|
||||
_, err = fs.Create(filepath.Join(spec.Active.MountPoint, "usr", "lib", "grub", "i386-pc", "modinfo.sh"))
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
mainDisk := sdkTypes.Disk{
|
||||
Name: "device",
|
||||
@ -349,9 +358,10 @@ var _ = Describe("Install action tests", func() {
|
||||
Expect(cl.WasGetCalledWith("http://my.config.org")).To(BeTrue())
|
||||
})
|
||||
|
||||
It("Fails on grub2-install errors", Label("grub"), func() {
|
||||
It("Fails to find grub2-install", Label("grub"), func() {
|
||||
spec.Target = device
|
||||
cmdFail = utils.FindCommand("grub2-install", []string{"grub2-install", "grub-install"})
|
||||
err := config.Fs.Remove(filepath.Join(spec.Active.MountPoint, "sbin", "grub2-install"))
|
||||
Expect(err).To(BeNil())
|
||||
Expect(installer.Run()).NotTo(BeNil())
|
||||
Expect(runner.MatchMilestones([][]string{{"grub2-install"}}))
|
||||
})
|
||||
@ -362,5 +372,12 @@ var _ = Describe("Install action tests", func() {
|
||||
Expect(installer.Run()).NotTo(BeNil())
|
||||
Expect(runner.MatchMilestones([][]string{{"tune2fs", "-L", constants.PassiveLabel}}))
|
||||
})
|
||||
It("Fails if there is no grub2 artifacts", Label("grub"), func() {
|
||||
spec.Target = device
|
||||
err := config.Fs.Remove(filepath.Join(spec.Active.MountPoint, "usr", "lib", "grub", "i386-pc", "modinfo.sh"))
|
||||
Expect(err).To(BeNil())
|
||||
Expect(installer.Run()).NotTo(BeNil())
|
||||
Expect(runner.MatchMilestones([][]string{{"grub2-install"}}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -130,7 +130,8 @@ var _ = Describe("Reset action tests", func() {
|
||||
ghwTest.AddDisk(mainDisk)
|
||||
ghwTest.CreateDevices()
|
||||
|
||||
fs.Create(constants.EfiDevice)
|
||||
Expect(fsutils.MkdirAll(fs, constants.EfiDevice, constants.DirPerm)).ToNot(HaveOccurred())
|
||||
|
||||
bootedFrom = constants.SystemLabel
|
||||
runner.SideEffect = func(cmd string, args ...string) ([]byte, error) {
|
||||
if cmd == cmdFail {
|
||||
|
433
pkg/action/sysext.go
Normal file
433
pkg/action/sysext.go
Normal file
@ -0,0 +1,433 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/distribution/reference"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos-sdk/types"
|
||||
"github.com/twpayne/go-vfs/v5"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// Implementation details for not trusted boot
|
||||
// sysext are stored under
|
||||
// /var/lib/kairos/extensions/
|
||||
// we link them to /var/lib/kairos/extensions/{active,passive} depending on where we want it to be enabled
|
||||
// Immucore on boot after mounting the persistent dir, will check those dirs\
|
||||
// it will then create the proper links to them under /run/extensions
|
||||
// This means they are enabled on boot and they are ephemeral, nothing is left behind in the actual sysext dirs
|
||||
// This prevents us from having to clean up in different dirs, we can just do cleaning in our dirs (remove links)
|
||||
// and on reboot they will not be enabled on boot
|
||||
// So all the actions (list, upgrade, download, remove) will be done on the persistent dir
|
||||
// And on boot we dinamycally link and enable them based on the boot type (active,passive) via immucore
|
||||
|
||||
// TODO: Check which extensions are running? is that possible?
|
||||
// TODO: On disable we should check if the extension is running and refresh systemd-sysext? YES
|
||||
// TODO: On remove we should check if the extension is running and refresh systemd-sysext? YES
|
||||
|
||||
const (
|
||||
sysextDir = "/var/lib/kairos/extensions/"
|
||||
sysextDirActive = "/var/lib/kairos/extensions/active"
|
||||
sysextDirPassive = "/var/lib/kairos/extensions/passive"
|
||||
sysextDirRecovery = "/var/lib/kairos/extensions/recovery"
|
||||
sysextDirCommon = "/var/lib/kairos/extensions/common"
|
||||
)
|
||||
|
||||
// SysExtension represents a system extension
|
||||
type SysExtension struct {
|
||||
Name string
|
||||
Location string
|
||||
}
|
||||
|
||||
func (s *SysExtension) String() string {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
// ListSystemExtensions lists the system extensions in the given directory
|
||||
// If none is passed then it shows the generic ones
|
||||
func ListSystemExtensions(cfg *config.Config, bootState string) ([]SysExtension, error) {
|
||||
switch bootState {
|
||||
case "active":
|
||||
cfg.Logger.Debug("Listing active system extensions")
|
||||
return getDirExtensions(cfg, sysextDirActive)
|
||||
case "passive":
|
||||
cfg.Logger.Debug("Listing passive system extensions")
|
||||
return getDirExtensions(cfg, sysextDirPassive)
|
||||
case "recovery":
|
||||
cfg.Logger.Debug("Listing recovery system extensions")
|
||||
return getDirExtensions(cfg, sysextDirRecovery)
|
||||
case "common":
|
||||
cfg.Logger.Debug("Listing common system extensions")
|
||||
return getDirExtensions(cfg, sysextDirCommon)
|
||||
default:
|
||||
cfg.Logger.Debug("Listing all system extensions (Enabled or not)")
|
||||
return getDirExtensions(cfg, sysextDir)
|
||||
}
|
||||
}
|
||||
|
||||
// getDirExtensions lists the system extensions in the given directory
|
||||
func getDirExtensions(cfg *config.Config, dir string) ([]SysExtension, error) {
|
||||
var out []SysExtension
|
||||
// get all the extensions in the sysextDir
|
||||
// Try to create the dir if it does not exist
|
||||
if _, err := cfg.Fs.Stat(dir); os.IsNotExist(err) {
|
||||
if err := vfs.MkdirAll(cfg.Fs, dir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("failed to create target dir %s: %w", dir, err)
|
||||
}
|
||||
}
|
||||
entries, err := cfg.Fs.ReadDir(dir)
|
||||
// We don't care if the dir does not exist, we just return an empty list
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() && filepath.Ext(entry.Name()) == ".raw" {
|
||||
out = append(out, SysExtension{Name: entry.Name(), Location: filepath.Join(dir, entry.Name())})
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetSystemExtension returns the system extension for a given name
|
||||
func GetSystemExtension(cfg *config.Config, name, bootState string) (SysExtension, error) {
|
||||
// Get a list of all installed system extensions
|
||||
installed, err := ListSystemExtensions(cfg, bootState)
|
||||
if err != nil {
|
||||
return SysExtension{}, err
|
||||
}
|
||||
// Check if the extension is installed
|
||||
// regex against the name
|
||||
re, err := regexp.Compile(name)
|
||||
if err != nil {
|
||||
return SysExtension{}, err
|
||||
}
|
||||
for _, ext := range installed {
|
||||
if re.MatchString(ext.Name) {
|
||||
return ext, nil
|
||||
}
|
||||
}
|
||||
// If not, return an error
|
||||
return SysExtension{}, fmt.Errorf("system extension %s not found", name)
|
||||
}
|
||||
|
||||
// EnableSystemExtension enables a system extension that is already in the system for a given bootstate
|
||||
// It creates a symlink to the extension in the target dir according to the bootstate given
|
||||
// It will create the target dir if it does not exist
|
||||
// It will check if the extension is already enabled but not fail if it is
|
||||
// It will check if the extension is installed
|
||||
// If now is true, it will enable the extension immediately by linking it to /run/extensions and refreshing systemd-sysext
|
||||
func EnableSystemExtension(cfg *config.Config, ext, bootState string, now bool) error {
|
||||
// first check if the extension is installed
|
||||
extension, err := GetSystemExtension(cfg, ext, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var targetDir string
|
||||
switch bootState {
|
||||
case "active":
|
||||
targetDir = sysextDirActive
|
||||
case "passive":
|
||||
targetDir = sysextDirPassive
|
||||
case "recovery":
|
||||
targetDir = sysextDirRecovery
|
||||
case "common":
|
||||
targetDir = sysextDirCommon
|
||||
default:
|
||||
return fmt.Errorf("boot state %s not supported", bootState)
|
||||
}
|
||||
|
||||
// Check if the target dir exists and create it if it doesn't
|
||||
if _, err := cfg.Fs.Stat(targetDir); os.IsNotExist(err) {
|
||||
if err := vfs.MkdirAll(cfg.Fs, targetDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create target dir %s: %w", targetDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the extension is already enabled
|
||||
enabled, err := GetSystemExtension(cfg, ext, bootState)
|
||||
// This doesnt fail if we have it already enabled
|
||||
if err == nil {
|
||||
if enabled.Name == extension.Name {
|
||||
cfg.Logger.Infof("System extension %s is already enabled in %s", extension.Name, bootState)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Create a symlink to the extension in the target dir
|
||||
if err := cfg.Fs.Symlink(extension.Location, filepath.Join(targetDir, extension.Name)); err != nil {
|
||||
return fmt.Errorf("failed to create symlink for %s: %w", extension.Name, err)
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s enabled in %s", extension.Name, bootState)
|
||||
|
||||
if now {
|
||||
// Check if the boot state is the same as the one we are enabling
|
||||
// This is to avoid enabling the extension in the wrong boot state
|
||||
_, stateMatches := cfg.Fs.Stat(fmt.Sprintf("/run/cos/%s_mode", bootState))
|
||||
// TODO: Check in UKI?
|
||||
cfg.Logger.Logger.Debug().Str("boot_state", bootState).Str("filecheck", fmt.Sprintf("/run/cos/%s_state", bootState)).Msg("Checking boot state")
|
||||
if stateMatches == nil || bootState == "common" {
|
||||
err = cfg.Fs.Symlink(filepath.Join(targetDir, extension.Name), filepath.Join("/run/extensions", extension.Name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create symlink for %s: %w", extension.Name, err)
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s enabled in /run/extensions", extension.Name)
|
||||
// It makes the sysext check the extension for a valid signature
|
||||
// Refresh systemd-sysext by restarting the service. As the config is set via the service overrides to nice things
|
||||
output, err := cfg.Runner.Run("systemctl", "restart", "systemd-sysext")
|
||||
if err != nil {
|
||||
cfg.Logger.Logger.Err(err).Str("output", string(output)).Msg("Failed to refresh systemd-sysext")
|
||||
return err
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s merged by systemd-sysext", extension.Name)
|
||||
} else {
|
||||
cfg.Logger.Infof("System extension %s enabled in %s but not merged by systemd-sysext as we are currently not booted in %s", extension.Name, bootState, bootState)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableSystemExtension disables a system extension that is already in the system for a given bootstate
|
||||
// It removes the symlink from the target dir according to the bootstate given
|
||||
func DisableSystemExtension(cfg *config.Config, ext string, bootState string, now bool) error {
|
||||
var targetDir string
|
||||
switch bootState {
|
||||
case "active":
|
||||
targetDir = sysextDirActive
|
||||
case "passive":
|
||||
targetDir = sysextDirPassive
|
||||
case "recovery":
|
||||
targetDir = sysextDirRecovery
|
||||
case "common":
|
||||
targetDir = sysextDirCommon
|
||||
default:
|
||||
return fmt.Errorf("boot state %s not supported", bootState)
|
||||
}
|
||||
|
||||
// Check if the target dir exists
|
||||
if _, err := cfg.Fs.Stat(targetDir); os.IsNotExist(err) {
|
||||
return fmt.Errorf("target dir %s does not exist", targetDir)
|
||||
}
|
||||
|
||||
// Check if the extension is enabled, do not fail if it is not
|
||||
extension, err := GetSystemExtension(cfg, ext, bootState)
|
||||
if err != nil {
|
||||
cfg.Logger.Infof("system extension %s is not enabled in %s", ext, bootState)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove the symlink
|
||||
if err := cfg.Fs.Remove(extension.Location); err != nil {
|
||||
return fmt.Errorf("failed to remove symlink for %s: %w", ext, err)
|
||||
}
|
||||
if now {
|
||||
// Check if the boot state is the same as the one we are disabling
|
||||
// This is to avoid disabling the extension in the wrong boot state
|
||||
_, stateMatches := cfg.Fs.Stat(fmt.Sprintf("/run/cos/%s_mode", bootState))
|
||||
cfg.Logger.Logger.Debug().Str("boot_state", bootState).Str("filecheck", fmt.Sprintf("/run/cos/%s_mode", bootState)).Msg("Checking boot state")
|
||||
if stateMatches == nil || bootState == "common" {
|
||||
// Remove the symlink from /run/extensions if is in there
|
||||
cfg.Logger.Logger.Debug().Str("stat", filepath.Join("/run/extensions", extension.Name)).Msg("Checking if symlink exists")
|
||||
_, stat := cfg.Fs.Readlink(filepath.Join("/run/extensions", extension.Name))
|
||||
if stat == nil {
|
||||
err = cfg.Fs.Remove(filepath.Join("/run/extensions", extension.Name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove symlink for %s: %w", extension.Name, err)
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s disabled from /run/extensions", extension.Name)
|
||||
// Now that its removed we refresh systemd-sysext
|
||||
output, err := cfg.Runner.Run("systemctl", "restart", "systemd-sysext")
|
||||
if err != nil {
|
||||
cfg.Logger.Logger.Err(err).Str("output", string(output)).Msg("Failed to refresh systemd-sysext")
|
||||
return err
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s refreshed by systemd-sysext", extension.Name)
|
||||
} else {
|
||||
cfg.Logger.Logger.Info().Msg("Extension not in /run/extensions, not refreshing")
|
||||
}
|
||||
} else {
|
||||
cfg.Logger.Infof("System extension %s disabled in %s but not refreshed by systemd-sysext as we are currently not booted in %s", extension.Name, bootState, bootState)
|
||||
}
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s disabled in %s", ext, bootState)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallSystemExtension installs a system extension from a given URI
|
||||
// It will download the extension and extract it to the target dir
|
||||
// It will check if the extension is already installed before doing anything
|
||||
func InstallSystemExtension(cfg *config.Config, uri string) error {
|
||||
// Parse the URI
|
||||
download, err := parseURI(cfg, uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse URI %s: %w", uri, err)
|
||||
}
|
||||
// Check if directory exists or create it
|
||||
if _, err := cfg.Fs.Stat(sysextDir); os.IsNotExist(err) {
|
||||
if err := vfs.MkdirAll(cfg.Fs, sysextDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create target dir %s: %w", sysextDir, err)
|
||||
}
|
||||
}
|
||||
// Download the extension
|
||||
if err := download.Download(sysextDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSystemExtension removes a system extension from the system
|
||||
// It will remove any symlinks to the extension
|
||||
// Then it will remove the extension
|
||||
// It will check if the extension is installed before doing anything
|
||||
func RemoveSystemExtension(cfg *config.Config, extension string, now bool) error {
|
||||
// Check if the extension is installed
|
||||
installed, err := GetSystemExtension(cfg, extension, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if installed.Name == "" && installed.Location == "" {
|
||||
cfg.Logger.Infof("System extension %s is not installed", extension)
|
||||
return nil
|
||||
}
|
||||
// Check if the extension is enabled in active or passive
|
||||
for _, state := range []string{"active", "passive", "recovery", "common"} {
|
||||
enabled, err := GetSystemExtension(cfg, extension, state)
|
||||
if err == nil {
|
||||
// Remove the symlink
|
||||
if err := cfg.Fs.Remove(enabled.Location); err != nil {
|
||||
return fmt.Errorf("failed to remove symlink for %s: %w", enabled.Name, err)
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s disabled from %s", enabled.Name, state)
|
||||
}
|
||||
}
|
||||
// Remove the extension
|
||||
if err := cfg.Fs.RemoveAll(installed.Location); err != nil {
|
||||
return fmt.Errorf("failed to remove extension %s: %w", installed.Name, err)
|
||||
}
|
||||
|
||||
if now {
|
||||
// Here as we are removing the extension we need to check if its in /run/extensions
|
||||
// We dont care about the bootState because we are removing it from all
|
||||
_, stat := cfg.Fs.Readlink(filepath.Join("/run/extensions", installed.Name))
|
||||
if stat == nil {
|
||||
err = cfg.Fs.Remove(filepath.Join("/run/extensions", installed.Name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to remove symlink for %s: %w", installed.Name, err)
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s removed from /run/extensions", installed.Name)
|
||||
// Now that its removed we refresh systemd-sysext
|
||||
output, err := cfg.Runner.Run("systemctl", "restart", "systemd-sysext")
|
||||
if err != nil {
|
||||
cfg.Logger.Logger.Err(err).Str("output", string(output)).Msg("Failed to refresh systemd-sysext")
|
||||
return err
|
||||
}
|
||||
cfg.Logger.Infof("System extension %s refreshed by systemd-sysext", installed.Name)
|
||||
} else {
|
||||
cfg.Logger.Logger.Info().Msg("Extension not in /run/extensions, not refreshing")
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Logger.Infof("System extension %s removed", installed.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseURI parses a URI and returns a SourceDownload
|
||||
// implementation based on the scheme of the URI
|
||||
func parseURI(cfg *config.Config, uri string) (SourceDownload, error) {
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scheme := u.Scheme
|
||||
value := u.Opaque
|
||||
if value == "" {
|
||||
value = filepath.Join(u.Host, u.Path)
|
||||
}
|
||||
switch scheme {
|
||||
case "oci", "docker", "container":
|
||||
n, err := reference.ParseNormalizedNamed(value)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid image reference %s", value)
|
||||
} else if reference.IsNameOnly(n) {
|
||||
value += ":latest"
|
||||
}
|
||||
return &dockerSource{value, cfg}, nil
|
||||
case "file":
|
||||
return &fileSource{value, cfg}, nil
|
||||
case "http", "https":
|
||||
// Pass the full uri including the protocol
|
||||
return &httpSource{uri, cfg}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid URI reference %s", uri)
|
||||
}
|
||||
}
|
||||
|
||||
// SourceDownload is an interface for downloading system extensions
|
||||
// from different sources. It allows for different implementations
|
||||
// for different sources of system extensions, such as files, directories,
|
||||
// or docker images. The interface defines a single method, Download,
|
||||
// which takes a destination path as an argument and returns an error
|
||||
type SourceDownload interface {
|
||||
Download(string) error
|
||||
}
|
||||
|
||||
// fileSource is a struct that implements the SourceDownload interface
|
||||
// for downloading system extensions from a file. It has two fields,
|
||||
// uri, which is the URI of the file to be downloaded and cfg which points to the Config
|
||||
// The Download method takes a destination path as an argument and returns an error if the
|
||||
// download fails.
|
||||
type fileSource struct {
|
||||
uri string
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
// Download just copies the file to the destination
|
||||
// As this is a file source, we just copy the file to the destination, not much to it
|
||||
func (f *fileSource) Download(dst string) error {
|
||||
src, err := f.cfg.Fs.ReadFile(f.uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file %s: %w", f.uri, err)
|
||||
}
|
||||
|
||||
stat, _ := f.cfg.Fs.Stat(f.uri)
|
||||
dstFile := filepath.Join(dst, filepath.Base(f.uri))
|
||||
f.cfg.Logger.Logger.Debug().Str("uri", f.uri).Str("target", dstFile).Msg("Copying system extension")
|
||||
// Keep original permissions
|
||||
if err = f.cfg.Fs.WriteFile(dstFile, src, stat.Mode()); err != nil {
|
||||
return fmt.Errorf("failed to copy file %s to %s: %w", f.uri, dstFile, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpSource struct {
|
||||
uri string
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func (h httpSource) Download(s string) error {
|
||||
// Download the file from the URI
|
||||
// and save it to the destination path
|
||||
h.cfg.Logger.Logger.Debug().Str("uri", h.uri).Str("target", filepath.Join(s, filepath.Base(h.uri))).Msg("Downloading system extension")
|
||||
return h.cfg.Client.GetURL(types.NewNullLogger(), h.uri, filepath.Join(s, filepath.Base(h.uri)))
|
||||
}
|
||||
|
||||
type dockerSource struct {
|
||||
uri string
|
||||
cfg *config.Config
|
||||
}
|
||||
|
||||
func (d dockerSource) Download(s string) error {
|
||||
// Download the file from the URI
|
||||
// and save it to the destination path
|
||||
err := d.cfg.ImageExtractor.ExtractImage(d.uri, s, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
588
pkg/action/sysext_test.go
Normal file
588
pkg/action/sysext_test.go
Normal file
@ -0,0 +1,588 @@
|
||||
package action_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/action"
|
||||
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
v1mock "github.com/kairos-io/kairos-agent/v2/tests/mocks"
|
||||
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/twpayne/go-vfs/v5"
|
||||
"github.com/twpayne/go-vfs/v5/vfst"
|
||||
)
|
||||
|
||||
var _ = Describe("Sysext Actions test", func() {
|
||||
var config *agentConfig.Config
|
||||
var runner *v1mock.FakeRunner
|
||||
var fs vfs.FS
|
||||
var logger sdkTypes.KairosLogger
|
||||
var mounter *v1mock.ErrorMounter
|
||||
var syscall *v1mock.FakeSyscall
|
||||
var httpClient *v1mock.FakeHTTPClient
|
||||
var cloudInit *v1mock.FakeCloudInitRunner
|
||||
var cleanup func()
|
||||
var memLog *bytes.Buffer
|
||||
var extractor *v1mock.FakeImageExtractor
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
runner = v1mock.NewFakeRunner()
|
||||
syscall = &v1mock.FakeSyscall{}
|
||||
mounter = v1mock.NewErrorMounter()
|
||||
httpClient = &v1mock.FakeHTTPClient{}
|
||||
memLog = &bytes.Buffer{}
|
||||
logger = sdkTypes.NewBufferLogger(memLog)
|
||||
logger.SetLevel("debug")
|
||||
extractor = v1mock.NewFakeImageExtractor(logger)
|
||||
cloudInit = &v1mock.FakeCloudInitRunner{}
|
||||
fs, cleanup, err = vfst.NewTestFS(map[string]interface{}{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err := vfs.MkdirAll(fs, "/var/lib/kairos/extensions", 0755)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = vfs.MkdirAll(fs, "/run/extensions", 0755)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Config object with all of our fakes on it
|
||||
config = agentConfig.NewConfig(
|
||||
agentConfig.WithFs(fs),
|
||||
agentConfig.WithRunner(runner),
|
||||
agentConfig.WithLogger(logger),
|
||||
agentConfig.WithMounter(mounter),
|
||||
agentConfig.WithSyscall(syscall),
|
||||
agentConfig.WithClient(httpClient),
|
||||
agentConfig.WithCloudInitRunner(cloudInit),
|
||||
agentConfig.WithImageExtractor(extractor),
|
||||
agentConfig.WithPlatform("linux/amd64"),
|
||||
)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
Describe("Listing extensions", func() {
|
||||
It("should NOT fail if the bootstate is not valid", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "invalid")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
Describe("With no dir", func() {
|
||||
BeforeEach(func() {
|
||||
err = config.Fs.RemoveAll("/var/lib/kairos/extensions")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
It("should return no extensions for installed extensions", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for active enabled extensions", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for passive enabled extensions", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for recovery enabled extensions", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "recovery")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for common enabled extensions", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "common")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
Describe("With empty dir", func() {
|
||||
It("should return no extensions", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for active enabled extensions", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for passive enabled extensions", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for recovery enabled extensions", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "recovery")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return no extensions for common enabled extensions", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "common")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
Describe("With dir with files", func() {
|
||||
It("should not return files that are not valid extensions", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/invalid", []byte("invalid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should return files that are valid extensions", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/valid.raw",
|
||||
},
|
||||
}))
|
||||
})
|
||||
It("should ONLY return files that are valid extensions", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/invalid", []byte("invalid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(extensions)).To(Equal(1))
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/valid.raw",
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
Describe("Enabling extensions", func() {
|
||||
It("should fail to enable a extension if bootState is not valid", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = action.EnableSystemExtension(config, "valid", "invalid", false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should enable an installed extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Passive should be empty
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
extensions, err = action.ListSystemExtensions(config, "recovery")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
extensions, err = action.ListSystemExtensions(config, "common")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for passive
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "passive", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Passive should have the extension
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/passive/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Check active again to see if it is still there
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Enable it for recovery
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "recovery", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Passive should have the extension
|
||||
extensions, err = action.ListSystemExtensions(config, "recovery")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/recovery/valid.raw",
|
||||
},
|
||||
}))
|
||||
|
||||
})
|
||||
It("should enable an installed extension and reload the system with it", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Fake the boot state
|
||||
Expect(config.Fs.Mkdir("/run/cos", 0755)).ToNot(HaveOccurred())
|
||||
Expect(config.Fs.WriteFile("/run/cos/active_mode", []byte("true"), 0644)).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// This basically returns an error if the command is not executed
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).To(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Passive should be empty
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Symlink should be created in /run/extensions
|
||||
_, err = config.Fs.Stat("/run/extensions/valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
readlink, err := config.Fs.Readlink("/run/extensions/valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Get the raw path as the readlink will return the real path, not the one in our fake fs
|
||||
realPath, err := config.Fs.RawPath("/var/lib/kairos/extensions/active/valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(readlink).To(Equal(realPath))
|
||||
})
|
||||
It("should enable an installed extension and reload the system with it if its a common one", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "common")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// This basically returns an error if the command is not executed
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).To(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for common
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "common", true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Should have refreshed the systemd-sysext
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).ToNot(HaveOccurred())
|
||||
// Should be enabled
|
||||
extensions, err = action.ListSystemExtensions(config, "common")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/common/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Active and Passive should be empty
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Symlink should be created in /run/extensions
|
||||
_, err = config.Fs.Stat("/run/extensions/valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("should enable an installed extension and NOT reload the system with it if we are on the wrong boot state", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// This basically returns an error if the command is not executed
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).To(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(runner.IncludesCmds([][]string{
|
||||
{"systemctl", "restart", "systemd-sysext"},
|
||||
})).To(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Passive should be empty
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Symlink should be created in /run/extensions
|
||||
_, err = config.Fs.Stat("/run/extensions/valid.raw")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should fail to enable a missing extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "invalid.raw", "active", false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Passive should be empty
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should not fail if the extension is already enabled", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
})
|
||||
Describe("Disabling extensions", func() {
|
||||
It("should fail if bootState is not valid", func() {
|
||||
err := action.DisableSystemExtension(config, "whatever", "invalid", false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("should disable an enabled extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Disable it
|
||||
err = action.DisableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should not fail to disable a not enabled extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
// Disable a non enabled extension
|
||||
err = action.DisableSystemExtension(config, "invalid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
Describe("Installing extensions", func() {
|
||||
Describe("With a file source", func() {
|
||||
It("should install a extension", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
err = config.Fs.WriteFile("/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = action.InstallSystemExtension(config, "file:///valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred(), memLog.String())
|
||||
// Check if the extension is installed
|
||||
extensions, err = action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/valid.raw",
|
||||
},
|
||||
}))
|
||||
})
|
||||
It("should fail to install a missing extension", func() {
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
err = action.InstallSystemExtension(config, "file:///invalid.raw")
|
||||
Expect(err).To(HaveOccurred(), memLog.String())
|
||||
// Check if the extension is installed
|
||||
extensions, err = action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
Describe("with a docker source", func() {
|
||||
It("should install a extension", func() {
|
||||
err = action.InstallSystemExtension(config, "docker://quay.io/valid:v1.0.0")
|
||||
Expect(err).ToNot(HaveOccurred(), memLog.String())
|
||||
expectedCall := v1mock.ExtractCall{ImageRef: "quay.io/valid:v1.0.0", Destination: "/var/lib/kairos/extensions/", PlatformRef: ""}
|
||||
Expect(extractor.WasCalledWithExtractCall(expectedCall)).To(BeTrue())
|
||||
})
|
||||
It("should fail to install a missing extension", func() {
|
||||
extractor.SideEffect = func(imageRef, destination, platformRef string) error {
|
||||
return fmt.Errorf("error")
|
||||
}
|
||||
err = action.InstallSystemExtension(config, "docker://quay.io/invalid:v1.0.0")
|
||||
Expect(err).To(HaveOccurred(), memLog.String())
|
||||
expectedCall := v1mock.ExtractCall{ImageRef: "quay.io/invalid:v1.0.0", Destination: "/var/lib/kairos/extensions/", PlatformRef: ""}
|
||||
Expect(extractor.WasCalledWithExtractCall(expectedCall)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
Describe("with a http source", func() {
|
||||
It("should install a extension", func() {
|
||||
err = action.InstallSystemExtension(config, "http://localhost:8080/valid.raw")
|
||||
Expect(err).ToNot(HaveOccurred(), memLog.String())
|
||||
Expect(httpClient.WasGetCalledWith("http://localhost:8080/valid.raw")).To(BeTrue())
|
||||
})
|
||||
It("should fail to install a missing extension", func() {
|
||||
httpClient.Error = true
|
||||
err = action.InstallSystemExtension(config, "http://localhost:8080/invalid.raw")
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(httpClient.WasGetCalledWith("http://localhost:8080/invalid.raw")).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
Describe("Removing extensions", func() {
|
||||
It("should remove an installed extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/valid.raw",
|
||||
},
|
||||
}))
|
||||
err = action.RemoveSystemExtension(config, "valid.raw", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should disable and remove an enabled extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Enable it for active
|
||||
err = action.EnableSystemExtension(config, "valid.raw", "active", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/active/valid.raw",
|
||||
},
|
||||
}))
|
||||
err = action.RemoveSystemExtension(config, "valid.raw", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// Check if it is removed from active
|
||||
extensions, err = action.ListSystemExtensions(config, "active")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Check if it is removed from passive
|
||||
extensions, err = action.ListSystemExtensions(config, "passive")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
// Check if it is removed from installed
|
||||
extensions, err = action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(BeEmpty())
|
||||
})
|
||||
It("should fail to remove a missing extension", func() {
|
||||
err = config.Fs.WriteFile("/var/lib/kairos/extensions/valid.raw", []byte("valid"), 0644)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
extensions, err := action.ListSystemExtensions(config, "")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(extensions).To(Equal([]action.SysExtension{
|
||||
{
|
||||
Name: "valid.raw",
|
||||
Location: "/var/lib/kairos/extensions/valid.raw",
|
||||
},
|
||||
}))
|
||||
err = action.RemoveSystemExtension(config, "invalid.raw", false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
@ -213,6 +213,13 @@ func contains(s []string, str string) bool {
|
||||
func (c Config) CheckForUsers() (err error) {
|
||||
// If nousers is enabled we do not check for the validity of the users and such
|
||||
// At this point, the config should be fully parsed and the yip stages ready
|
||||
|
||||
// Check if the sentinel is present
|
||||
_, sentinel := c.Fs.Stat("/etc/kairos/.nousers")
|
||||
if sentinel == nil {
|
||||
c.Logger.Logger.Debug().Msg("Sentinel file found, skipping user check")
|
||||
return nil
|
||||
}
|
||||
if !c.Install.NoUsers {
|
||||
anyAdmin := false
|
||||
cc, _ := c.Config.String()
|
||||
|
@ -126,7 +126,8 @@ const (
|
||||
StateResetBootSuffix = " state reset (auto)"
|
||||
|
||||
// Error
|
||||
UpgradeNoSourceError = "Could not find a proper source for the upgrade.\nThis can be configured in the cloud config files under the 'upgrade.system.uri' key or via cmdline using the '--source' flag."
|
||||
UpgradeNoSourceError = "could not find a proper source for the upgrade.\nThis can be configured in the cloud config files under the 'upgrade.system.uri' key or via cmdline using the '--source' flag"
|
||||
UpgradeRecoveryNoSourceError = "could not find a proper source for the recovery upgrade.\nThis can be configured in the cloud config files under the 'upgrade.recovery-system.uri' key or via cmdline using the '--source' flag"
|
||||
MultipleEntriesAssessmentError = "multiple boot entries found for %s"
|
||||
NoBootAssessmentWarning = "No boot assessment found in current boot entry config file"
|
||||
)
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
diskfs "github.com/diskfs/go-diskfs/disk"
|
||||
"github.com/diskfs/go-diskfs/partition/gpt"
|
||||
agentConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
cnst "github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
@ -49,7 +48,7 @@ func NewElemental(config *agentConfig.Config) *Elemental {
|
||||
// FormatPartition will format an already existing partition
|
||||
func (e *Elemental) FormatPartition(part *types.Partition, opts ...string) error {
|
||||
e.config.Logger.Infof("Formatting '%s' partition", part.FilesystemLabel)
|
||||
return partitioner.FormatDevice(e.config.Runner, part.Path, part.FS, part.FilesystemLabel, opts...)
|
||||
return partitioner.FormatDevice(e.config.Logger, e.config.Runner, part.Path, part.FS, part.FilesystemLabel, opts...)
|
||||
}
|
||||
|
||||
// PartitionAndFormatDevice creates a new empty partition table on target disk
|
||||
@ -73,13 +72,10 @@ func (e *Elemental) PartitionAndFormatDevice(i v1.SharedInstallSpec) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only re-read table on devices. On files there is no need and this call will fail
|
||||
if disk.Type == diskfs.Device {
|
||||
err = disk.ReReadPartitionTable()
|
||||
if err != nil {
|
||||
e.config.Logger.Errorf("Reread table: %s", err)
|
||||
return err
|
||||
}
|
||||
err = disk.ReReadPartitionTable()
|
||||
if err != nil {
|
||||
e.config.Logger.Errorf("Reread table: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
table, err := disk.GetPartitionTable()
|
||||
@ -121,7 +117,7 @@ func (e *Elemental) PartitionAndFormatDevice(i v1.SharedInstallSpec) error {
|
||||
if err != nil {
|
||||
e.config.Logger.Errorf("Failed finding partition %s by partition label: %s", configPart.FilesystemLabel, err)
|
||||
}
|
||||
err = partitioner.FormatDevice(e.config.Runner, device, configPart.FS, configPart.FilesystemLabel)
|
||||
err = partitioner.FormatDevice(e.config.Logger, e.config.Runner, device, configPart.FS, configPart.FilesystemLabel)
|
||||
if err != nil {
|
||||
e.config.Logger.Errorf("Failed formatting partition: %s", err)
|
||||
return err
|
||||
@ -588,7 +584,7 @@ func (e Elemental) SetDefaultGrubEntry(partMountPoint string, imgMountPoint stri
|
||||
return utils.SetPersistentVariables(
|
||||
filepath.Join(partMountPoint, cnst.GrubOEMEnv),
|
||||
map[string]string{"default_menu_entry": defaultEntry},
|
||||
e.config.Fs,
|
||||
e.config,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -20,10 +20,10 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/diskfs/go-diskfs"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
sc "syscall"
|
||||
"testing"
|
||||
@ -38,7 +38,7 @@ import (
|
||||
ghwMock "github.com/kairos-io/kairos-sdk/ghw/mocks"
|
||||
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
||||
|
||||
"github.com/diskfs/go-diskfs"
|
||||
fileBackend "github.com/diskfs/go-diskfs/backend/file"
|
||||
"github.com/diskfs/go-diskfs/partition/gpt"
|
||||
"github.com/gofrs/uuid"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@ -364,7 +364,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
Expect(err).To(BeNil())
|
||||
Expect(os.RemoveAll(filepath.Join(tmpDir, "/test.img"))).ToNot(HaveOccurred())
|
||||
// at least 2Gb in size as state is set to 1G
|
||||
_, err = diskfs.Create(filepath.Join(tmpDir, "/test.img"), 2*1024*1024*1024, diskfs.Raw, 512)
|
||||
_, err = fileBackend.CreateFromPath(filepath.Join(tmpDir, "/test.img"), 2*1024*1024*1024)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
config.Install.Device = filepath.Join(tmpDir, "/test.img")
|
||||
install, err = agentConfig.NewInstallSpec(config)
|
||||
@ -382,17 +382,18 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
install.Firmware = v1.EFI
|
||||
Expect(install.Partitions.SetFirmwarePartitions(v1.EFI, v1.GPT)).To(BeNil())
|
||||
Expect(el.PartitionAndFormatDevice(install)).To(BeNil())
|
||||
disk, err := diskfs.Open(filepath.Join(tmpDir, "/test.img"), diskfs.WithOpenMode(diskfs.ReadOnly))
|
||||
disk, err := fileBackend.OpenFromPath(filepath.Join(tmpDir, "/test.img"), true)
|
||||
defer disk.Close()
|
||||
table, err := gpt.Read(disk, int(diskfs.SectorSize512), int(diskfs.SectorSize512))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// check that its type GPT
|
||||
Expect(reflect.TypeOf(disk.Table)).To(Equal(reflect.TypeOf(&gpt.Table{})))
|
||||
Expect(table.Type()).To(Equal("gpt"))
|
||||
// Expect the disk UUID to be constant
|
||||
Expect(strings.ToLower(disk.Table.UUID())).To(Equal(strings.ToLower(cnst.DiskUUID)))
|
||||
Expect(strings.ToLower(table.UUID())).To(Equal(strings.ToLower(cnst.DiskUUID)))
|
||||
// 5 partitions (boot, oem, recovery, state and persistent)
|
||||
Expect(len(disk.Table.GetPartitions())).To(Equal(5))
|
||||
Expect(len(table.GetPartitions())).To(Equal(5))
|
||||
// Cast the boot partition into specific type to check the type and such
|
||||
part := disk.Table.GetPartitions()[0]
|
||||
part := table.GetPartitions()[0]
|
||||
partition, ok := part.(*gpt.Partition)
|
||||
Expect(ok).To(BeTrue())
|
||||
// Should be efi type
|
||||
@ -402,8 +403,8 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
// Should have predictable UUID
|
||||
Expect(strings.ToLower(partition.UUID())).To(Equal(strings.ToLower(uuid.NewV5(uuid.NamespaceURL, cnst.EfiLabel).String())))
|
||||
// Check the rest have the proper types
|
||||
for i := 1; i < len(disk.Table.GetPartitions()); i++ {
|
||||
part := disk.Table.GetPartitions()[i]
|
||||
for i := 1; i < len(table.GetPartitions()); i++ {
|
||||
part := table.GetPartitions()[i]
|
||||
partition, ok := part.(*gpt.Partition)
|
||||
Expect(ok).To(BeTrue())
|
||||
// all of them should have the Linux fs type
|
||||
@ -415,17 +416,19 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
install.Firmware = v1.BIOS
|
||||
Expect(install.Partitions.SetFirmwarePartitions(v1.BIOS, v1.GPT)).To(BeNil())
|
||||
Expect(el.PartitionAndFormatDevice(install)).To(BeNil())
|
||||
disk, err := diskfs.Open(filepath.Join(tmpDir, "/test.img"), diskfs.WithOpenMode(diskfs.ReadOnly))
|
||||
disk, err := fileBackend.OpenFromPath(filepath.Join(tmpDir, "/test.img"), true)
|
||||
defer disk.Close()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
table, err := gpt.Read(disk, int(diskfs.SectorSize512), int(diskfs.SectorSize512))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// check that its type GPT
|
||||
Expect(reflect.TypeOf(disk.Table)).To(Equal(reflect.TypeOf(&gpt.Table{})))
|
||||
Expect(table.Type()).To(Equal("gpt"))
|
||||
// Expect the disk UUID to be constant
|
||||
Expect(strings.ToLower(disk.Table.UUID())).To(Equal(strings.ToLower(cnst.DiskUUID)))
|
||||
Expect(strings.ToLower(table.UUID())).To(Equal(strings.ToLower(cnst.DiskUUID)))
|
||||
// 5 partitions (boot, oem, recovery, state and persistent)
|
||||
Expect(len(disk.Table.GetPartitions())).To(Equal(5))
|
||||
Expect(len(table.GetPartitions())).To(Equal(5))
|
||||
// Cast the boot partition into specific type to check the type and such
|
||||
part := disk.Table.GetPartitions()[0]
|
||||
part := table.GetPartitions()[0]
|
||||
partition, ok := part.(*gpt.Partition)
|
||||
Expect(ok).To(BeTrue())
|
||||
// Should be BIOS boot type
|
||||
@ -434,8 +437,8 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
Expect(partition.Name).To(Equal(cnst.BiosPartName))
|
||||
// Should have predictable UUID
|
||||
Expect(strings.ToLower(partition.UUID())).To(Equal(strings.ToLower(uuid.NewV5(uuid.NamespaceURL, cnst.EfiLabel).String())))
|
||||
for i := 1; i < len(disk.Table.GetPartitions()); i++ {
|
||||
part := disk.Table.GetPartitions()[i]
|
||||
for i := 1; i < len(table.GetPartitions()); i++ {
|
||||
part := table.GetPartitions()[i]
|
||||
partition, ok := part.(*gpt.Partition)
|
||||
Expect(ok).To(BeTrue())
|
||||
// all of them should have the Linux fs type
|
||||
@ -845,7 +848,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
el := elemental.NewElemental(config)
|
||||
Expect(config.Fs.Mkdir("/tmp", cnst.DirPerm)).To(BeNil())
|
||||
Expect(el.SetDefaultGrubEntry("/tmp", "/imgMountpoint", "dio")).To(BeNil())
|
||||
varsParsed, err := utils.ReadPersistentVariables(filepath.Join("/tmp", cnst.GrubOEMEnv), config.Fs)
|
||||
varsParsed, err := utils.ReadPersistentVariables(filepath.Join("/tmp", cnst.GrubOEMEnv), config)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(varsParsed["default_menu_entry"]).To(Equal("dio"))
|
||||
})
|
||||
@ -853,7 +856,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
el := elemental.NewElemental(config)
|
||||
Expect(config.Fs.Mkdir("/mountpoint", cnst.DirPerm)).To(BeNil())
|
||||
Expect(el.SetDefaultGrubEntry("/mountpoint", "/imgMountPoint", "")).To(BeNil())
|
||||
_, err := utils.ReadPersistentVariables(filepath.Join("/tmp", cnst.GrubOEMEnv), config.Fs)
|
||||
_, err := utils.ReadPersistentVariables(filepath.Join("/tmp", cnst.GrubOEMEnv), config)
|
||||
// Because it didnt do anything due to the entry being empty, the file should not be there
|
||||
Expect(err).ToNot(BeNil())
|
||||
_, err = config.Fs.Stat(filepath.Join("/tmp", cnst.GrubOEMEnv))
|
||||
@ -868,7 +871,7 @@ var _ = Describe("Elemental", Label("elemental"), func() {
|
||||
|
||||
el := elemental.NewElemental(config)
|
||||
Expect(el.SetDefaultGrubEntry("/mountpoint", "/imgMountPoint", "")).To(BeNil())
|
||||
varsParsed, err := utils.ReadPersistentVariables(filepath.Join("/mountpoint", cnst.GrubOEMEnv), config.Fs)
|
||||
varsParsed, err := utils.ReadPersistentVariables(filepath.Join("/mountpoint", cnst.GrubOEMEnv), config)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(varsParsed["default_menu_entry"]).To(Equal("test"))
|
||||
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"regexp"
|
||||
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos-sdk/types"
|
||||
)
|
||||
|
||||
type MkfsCall struct {
|
||||
@ -77,11 +78,11 @@ func (mkfs MkfsCall) Apply() (string, error) {
|
||||
}
|
||||
|
||||
// FormatDevice formats a block device with the given parameters
|
||||
func FormatDevice(runner v1.Runner, device string, fileSystem string, label string, opts ...string) error {
|
||||
func FormatDevice(logger types.KairosLogger, runner v1.Runner, device string, fileSystem string, label string, opts ...string) error {
|
||||
mkfs := MkfsCall{fileSystem: fileSystem, label: label, customOpts: opts, dev: device, runner: runner}
|
||||
out, err := mkfs.Apply()
|
||||
if err != nil {
|
||||
fmt.Println(out)
|
||||
logger.Logger.Warn().Err(err).Str("output", out).Msg("mkfs failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ func (u *UpgradeSpec) RecoveryUpgrade() bool {
|
||||
func (u *UpgradeSpec) Sanitize() error {
|
||||
if u.RecoveryUpgrade() {
|
||||
if u.Recovery.Source.IsEmpty() {
|
||||
return fmt.Errorf(constants.UpgradeNoSourceError)
|
||||
return fmt.Errorf(constants.UpgradeRecoveryNoSourceError)
|
||||
}
|
||||
if u.Partitions.Recovery == nil || u.Partitions.Recovery.MountPoint == "" {
|
||||
return fmt.Errorf("undefined recovery partition")
|
||||
|
@ -17,9 +17,10 @@ limitations under the License.
|
||||
package v1_test
|
||||
|
||||
import (
|
||||
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
||||
"path/filepath"
|
||||
|
||||
sdkTypes "github.com/kairos-io/kairos-sdk/types"
|
||||
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
@ -451,7 +452,7 @@ var _ = Describe("Types", Label("types", "config"), func() {
|
||||
It("fails with empty source", func() {
|
||||
err := spec.Sanitize()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring(constants.UpgradeNoSourceError))
|
||||
Expect(err.Error()).To(ContainSubstring(constants.UpgradeRecoveryNoSourceError))
|
||||
})
|
||||
It("fails with missing recovery partition", func() {
|
||||
spec.Recovery.Source = v1.NewFileSrc("/tmp")
|
||||
|
@ -31,6 +31,7 @@ type FS interface {
|
||||
RemoveAll(path string) error
|
||||
ReadFile(filename string) ([]byte, error)
|
||||
Readlink(name string) (string, error)
|
||||
Symlink(oldname, newname string) error
|
||||
RawPath(name string) (string, error)
|
||||
ReadDir(dirname string) ([]fs.DirEntry, error)
|
||||
Remove(name string) error
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
)
|
||||
|
||||
@ -30,7 +31,19 @@ type OCIImageExtractor struct{}
|
||||
var _ ImageExtractor = OCIImageExtractor{}
|
||||
|
||||
func (e OCIImageExtractor) ExtractImage(imageRef, destination, platformRef string) error {
|
||||
img, err := utils.GetImage(imageRef, utils.GetCurrentPlatform(), nil, nil)
|
||||
// If we pass a platform
|
||||
if platformRef != "" {
|
||||
// make sure its correct
|
||||
_, err := v1.ParsePlatform(platformRef)
|
||||
if err != nil {
|
||||
// and if we cannot properly parse it, then default to the current platform
|
||||
platformRef = utils.GetCurrentPlatform()
|
||||
}
|
||||
} else {
|
||||
// if we don't pass a platform, then default to the current platform
|
||||
platformRef = utils.GetCurrentPlatform()
|
||||
}
|
||||
img, err := utils.GetImage(imageRef, platformRef, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -185,7 +185,6 @@ func SyncData(log sdkTypes.KairosLogger, runner v1.Runner, fs v1.FS, source stri
|
||||
"--human-readable",
|
||||
"--archive", // recursive, symbolic links, permissions, owner, group, modification times, device files, special files
|
||||
"--acls", // preserve ACLS and permissions
|
||||
"--atimes", // preserve access times
|
||||
}
|
||||
|
||||
for _, e := range excludes {
|
||||
@ -504,11 +503,27 @@ func CalcFileChecksum(fs v1.FS, fileName string) (string, error) {
|
||||
|
||||
// FindCommand will search for the command(s) in the options given to find the current command
|
||||
// If it cant find it returns the default value give. Useful for the same binaries with different names across OS
|
||||
func FindCommand(defaultPath string, options []string) string {
|
||||
func FindCommand(fs v1.FS, defaultPath string, options []string) string {
|
||||
for _, p := range options {
|
||||
path, err := exec.LookPath(p)
|
||||
if err == nil {
|
||||
return path
|
||||
// If its a full path, check if it exists directly
|
||||
if strings.Contains(p, "/") {
|
||||
d, err := fs.Stat(p)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if d.IsDir() {
|
||||
continue
|
||||
}
|
||||
m := d.Mode()
|
||||
// Check if its executable
|
||||
if m&0111 != 0 {
|
||||
return p
|
||||
}
|
||||
} else {
|
||||
path, err := exec.LookPath(p)
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -61,16 +62,38 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
|
||||
if !efi {
|
||||
g.config.Logger.Info("Installing GRUB..")
|
||||
|
||||
// Find where in the rootDir the grub2 files for i386-pc are
|
||||
// Use the modinfo.sh as a marker
|
||||
grubdir = findGrubDir(g.config.Fs, rootDir)
|
||||
if grubdir == "" {
|
||||
g.config.Logger.Logger.Error().Str("path", rootDir).Msg("Failed to find grub dir")
|
||||
return fmt.Errorf("failed to find grub dir under %s", rootDir)
|
||||
}
|
||||
|
||||
grubargs = append(
|
||||
grubargs,
|
||||
fmt.Sprintf("--root-directory=%s", rootDir),
|
||||
fmt.Sprintf("--directory=%s", grubdir),
|
||||
fmt.Sprintf("--boot-directory=%s", bootDir),
|
||||
"--target=i386-pc",
|
||||
target,
|
||||
)
|
||||
|
||||
g.config.Logger.Debugf("Running grub with the following args: %s", grubargs)
|
||||
out, err := g.config.Runner.Run(FindCommand("grub2-install", []string{"grub2-install", "grub-install"}), grubargs...)
|
||||
|
||||
grubBin := FindCommand(g.config.Fs, "", []string{
|
||||
filepath.Join(rootDir, "/usr/sbin/", "grub2-install"),
|
||||
filepath.Join(rootDir, "/usr/bin/", "grub2-install"),
|
||||
filepath.Join(rootDir, "/sbin/", "grub2-install"),
|
||||
filepath.Join(rootDir, "/usr/sbin/", "grub-install"),
|
||||
filepath.Join(rootDir, "/usr/bin/", "grub-install"),
|
||||
filepath.Join(rootDir, "/sbin/", "grub-install"),
|
||||
})
|
||||
g.config.Logger.Logger.Debug().Str("command", grubBin).Msg("Found grub binary")
|
||||
if grubBin == "" {
|
||||
g.config.Logger.Logger.Error().Str("path", rootDir).Msg("Grub binary not found in path")
|
||||
return fmt.Errorf("grub binary not found in path")
|
||||
}
|
||||
out, err := g.config.Runner.Run(grubBin, grubargs...)
|
||||
if err != nil {
|
||||
g.config.Logger.Errorf(string(out))
|
||||
return err
|
||||
@ -257,20 +280,62 @@ func (g Grub) Install(target, rootDir, bootDir, grubConf, tty string, efi bool,
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPersistentVariables sets the given vars into the given grubEnvFile for grub to read them
|
||||
func SetPersistentVariables(grubEnvFile string, vars map[string]string, fs v1.FS) error {
|
||||
// findGrubDir will find the grub dir under the dir given if possible by searching for the modinfo.sh
|
||||
// And it will return the full dir path where the modinfo.sh is contained
|
||||
func findGrubDir(vfs v1.FS, dir string) string {
|
||||
var foundPath string
|
||||
_ = fsutils.WalkDirFs(vfs, dir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Name() == "modinfo.sh" && strings.Contains(path, "i386-pc") {
|
||||
// We found the grub dir, return it
|
||||
foundPath = filepath.Dir(path)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return foundPath
|
||||
|
||||
}
|
||||
|
||||
func SetPersistentVariables(grubEnvFile string, vars map[string]string, c *agentConfig.Config) error {
|
||||
var b bytes.Buffer
|
||||
// Write header
|
||||
b.WriteString("# GRUB Environment Block\n")
|
||||
|
||||
keys := make([]string, 0, len(vars))
|
||||
for k := range vars {
|
||||
// First we need to read the existing values from the grubenv file if they exist
|
||||
finalVars, err := ReadPersistentVariables(grubEnvFile, c)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("error reading existing grubenv file %s: %s", grubEnvFile, err)
|
||||
}
|
||||
}
|
||||
// Check if we have a nil var
|
||||
if len(finalVars) != 0 {
|
||||
c.Logger.Logger.Debug().Interface("existingVars", finalVars).Msg("Existing grubenv variables")
|
||||
}
|
||||
|
||||
// Merge the existing vars with the new ones
|
||||
// existing vars will be overridden by the new ones from vars if they match
|
||||
for key, newValue := range vars {
|
||||
if oldValue, exists := finalVars[key]; exists {
|
||||
c.Logger.Logger.Warn().Str("key", key).Str("oldValue", oldValue).Str("newValue", newValue).Msg("Overriding existing grubenv variable")
|
||||
}
|
||||
finalVars[key] = newValue
|
||||
}
|
||||
|
||||
keys := make([]string, 0, len(finalVars))
|
||||
|
||||
for k := range finalVars {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
if len(vars[k]) > 0 {
|
||||
b.WriteString(fmt.Sprintf("%s=%s\n", k, vars[k]))
|
||||
if len(finalVars[k]) > 0 {
|
||||
b.WriteString(fmt.Sprintf("%s=%s\n", k, finalVars[k]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -280,7 +345,7 @@ func SetPersistentVariables(grubEnvFile string, vars map[string]string, fs v1.FS
|
||||
for i := 0; i < toBeFilled; i++ {
|
||||
b.WriteByte('#')
|
||||
}
|
||||
return fs.WriteFile(grubEnvFile, b.Bytes(), cnst.FilePerm)
|
||||
return c.Fs.WriteFile(grubEnvFile, b.Bytes(), cnst.FilePerm)
|
||||
}
|
||||
|
||||
// copyGrubFonts will try to finds and copy the needed grub fonts into the system
|
||||
@ -397,11 +462,12 @@ func (g Grub) copyGrub() error {
|
||||
}
|
||||
|
||||
// ReadPersistentVariables will read a grub env file and parse the values
|
||||
func ReadPersistentVariables(grubEnvFile string, fs v1.FS) (map[string]string, error) {
|
||||
func ReadPersistentVariables(grubEnvFile string, c *agentConfig.Config) (map[string]string, error) {
|
||||
vars := make(map[string]string)
|
||||
f, err := fs.ReadFile(grubEnvFile)
|
||||
|
||||
f, err := c.Fs.ReadFile(grubEnvFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return vars, err
|
||||
}
|
||||
for _, a := range strings.Split(string(f), "\n") {
|
||||
// comment or fillup, so skip
|
||||
@ -412,7 +478,7 @@ func ReadPersistentVariables(grubEnvFile string, fs v1.FS) (map[string]string, e
|
||||
if len(splitted) == 2 {
|
||||
vars[splitted[0]] = splitted[1]
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid format for %s", a)
|
||||
return vars, fmt.Errorf("invalid format for %s", a)
|
||||
}
|
||||
}
|
||||
return vars, nil
|
||||
|
@ -162,7 +162,6 @@ func GetPartitionViaDM(fs v1.FS, label string) *types.Partition {
|
||||
if len(slaves) == 1 {
|
||||
// We got the partition this dm is associated to, now lets read that partition udev identifier
|
||||
partNumber, err := fs.ReadFile(filepath.Join(lp.SysBlock, dev.Name(), "slaves", slaves[0].Name(), "dev"))
|
||||
fmt.Println(string(partNumber))
|
||||
// If no errors and partNumber not empty read the device from udev
|
||||
if err == nil || string(partNumber) != "" {
|
||||
// Now for some magic!
|
||||
@ -173,7 +172,6 @@ func GetPartitionViaDM(fs v1.FS, label string) *types.Partition {
|
||||
// extract the udevInfo called ID_PART_ENTRY_DISK which gives us the udev ID of the parent disk
|
||||
baseID := strings.Split(strings.TrimSpace(string(partNumber)), ":")
|
||||
udevID = fmt.Sprintf("b%s:0", baseID[0])
|
||||
fmt.Printf("Reading udevdata of device: %s\n", filepath.Join(lp.RunUdevData, udevID))
|
||||
// Read udev info about this device
|
||||
udevBytes, _ = fs.ReadFile(filepath.Join(lp.RunUdevData, udevID))
|
||||
udevInfo = make(map[string]string)
|
||||
|
@ -691,6 +691,17 @@ var _ = Describe("Utils", Label("utils"), func() {
|
||||
|
||||
err = fs.WriteFile(filepath.Join(rootDir, constants.GrubConf), []byte("console=tty1"), 0644)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
// Create fake grub dir in rootfs and fake grub binaries
|
||||
err = fsutils.MkdirAll(fs, filepath.Join(rootDir, "sbin"), constants.DirPerm)
|
||||
Expect(err).To(BeNil())
|
||||
f, err := fs.Create(filepath.Join(rootDir, "sbin", "grub2-install"))
|
||||
Expect(err).To(BeNil())
|
||||
Expect(f.Chmod(0755)).ToNot(HaveOccurred())
|
||||
err = fsutils.MkdirAll(fs, filepath.Join(rootDir, "usr", "lib", "grub", "i386-pc"), constants.DirPerm)
|
||||
Expect(err).To(BeNil())
|
||||
_, err = fs.Create(filepath.Join(rootDir, "usr", "lib", "grub", "i386-pc", "modinfo.sh"))
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
})
|
||||
It("installs with default values", func() {
|
||||
grub := utils.NewGrub(config)
|
||||
@ -741,6 +752,18 @@ var _ = Describe("Utils", Label("utils"), func() {
|
||||
Expect(err).To(BeNil())
|
||||
|
||||
})
|
||||
It("fails with bios if no grub2-install file exists", func() {
|
||||
Expect(fs.RemoveAll(filepath.Join(rootDir, "sbin", "grub2-install"))).ToNot(HaveOccurred())
|
||||
grub := utils.NewGrub(config)
|
||||
err := grub.Install(target, rootDir, bootDir, constants.GrubConf, "", false, "")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("fails with bios if no modules files exists", func() {
|
||||
Expect(fs.RemoveAll(filepath.Join(rootDir, "usr", "lib", "grub", "i386-pc"))).ToNot(HaveOccurred())
|
||||
grub := utils.NewGrub(config)
|
||||
err := grub.Install(target, rootDir, bootDir, constants.GrubConf, "", false, "")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
It("fails with efi if no modules files exist", Label("efi"), func() {
|
||||
grub := utils.NewGrub(config)
|
||||
err := grub.Install(target, rootDir, bootDir, constants.GrubConf, "", true, "")
|
||||
@ -804,9 +827,9 @@ var _ = Describe("Utils", Label("utils"), func() {
|
||||
defer os.Remove(temp.Name())
|
||||
Expect(utils.SetPersistentVariables(
|
||||
temp.Name(), map[string]string{"key1": "value1", "key2": "value2"},
|
||||
config.Fs,
|
||||
config,
|
||||
)).To(BeNil())
|
||||
readVars, err := utils.ReadPersistentVariables(temp.Name(), config.Fs)
|
||||
readVars, err := utils.ReadPersistentVariables(temp.Name(), config)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(readVars["key1"]).To(Equal("value1"))
|
||||
Expect(readVars["key2"]).To(Equal("value2"))
|
||||
@ -814,10 +837,35 @@ var _ = Describe("Utils", Label("utils"), func() {
|
||||
It("Fails setting variables", func() {
|
||||
e := utils.SetPersistentVariables(
|
||||
"badfilenopath", map[string]string{"key1": "value1"},
|
||||
config.Fs,
|
||||
config,
|
||||
)
|
||||
Expect(e).NotTo(BeNil())
|
||||
})
|
||||
It("respects existing variables", func() {
|
||||
temp, err := os.CreateTemp("", "grub-*")
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
defer os.Remove(temp.Name())
|
||||
Expect(utils.SetPersistentVariables(
|
||||
temp.Name(), map[string]string{"key1": "value1", "key2": "value2"},
|
||||
config,
|
||||
)).To(BeNil())
|
||||
readVars, err := utils.ReadPersistentVariables(temp.Name(), config)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(readVars["key1"]).To(Equal("value1"))
|
||||
Expect(readVars["key2"]).To(Equal("value2"))
|
||||
|
||||
// Now we do it again with a different value
|
||||
Expect(utils.SetPersistentVariables(
|
||||
temp.Name(), map[string]string{"key1": "value3", "key3": "value4"},
|
||||
config,
|
||||
)).To(BeNil())
|
||||
// Now there should be an extra key and key1 should be updated
|
||||
readVars, err = utils.ReadPersistentVariables(temp.Name(), config)
|
||||
Expect(err).To(BeNil())
|
||||
Expect(readVars["key1"]).To(Equal("value3"))
|
||||
Expect(readVars["key2"]).To(Equal("value2"))
|
||||
Expect(readVars["key3"]).To(Equal("value4"))
|
||||
})
|
||||
})
|
||||
})
|
||||
Describe("CreateSquashFS", Label("CreateSquashFS"), func() {
|
||||
|
@ -22,15 +22,22 @@ import (
|
||||
)
|
||||
|
||||
type FakeImageExtractor struct {
|
||||
Logger sdkTypes.KairosLogger
|
||||
SideEffect func(imageRef, destination, platformRef string) error
|
||||
Logger sdkTypes.KairosLogger
|
||||
SideEffect func(imageRef, destination, platformRef string) error
|
||||
ClientCalls []ExtractCall
|
||||
}
|
||||
|
||||
func (f FakeImageExtractor) GetOCIImageSize(imageRef, platformRef string) (int64, error) {
|
||||
type ExtractCall struct {
|
||||
ImageRef string
|
||||
Destination string
|
||||
PlatformRef string
|
||||
}
|
||||
|
||||
func (f *FakeImageExtractor) GetOCIImageSize(imageRef, platformRef string) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var _ v1.ImageExtractor = FakeImageExtractor{}
|
||||
var _ v1.ImageExtractor = &FakeImageExtractor{}
|
||||
|
||||
func NewFakeImageExtractor(logger sdkTypes.KairosLogger) *FakeImageExtractor {
|
||||
l := logger
|
||||
@ -42,8 +49,9 @@ func NewFakeImageExtractor(logger sdkTypes.KairosLogger) *FakeImageExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
func (f FakeImageExtractor) ExtractImage(imageRef, destination, platformRef string) error {
|
||||
func (f *FakeImageExtractor) ExtractImage(imageRef, destination, platformRef string) error {
|
||||
f.Logger.Debugf("extracting %s to %s in platform %s", imageRef, destination, platformRef)
|
||||
f.ClientCalls = append(f.ClientCalls, ExtractCall{ImageRef: imageRef, Destination: destination, PlatformRef: platformRef})
|
||||
if f.SideEffect != nil {
|
||||
f.Logger.Debugf("running side effect")
|
||||
return f.SideEffect(imageRef, destination, platformRef)
|
||||
@ -51,3 +59,24 @@ func (f FakeImageExtractor) ExtractImage(imageRef, destination, platformRef stri
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WasCalledWithImageRef is a helper method to confirm that the client was called with the given image ref
|
||||
func (f *FakeImageExtractor) WasCalledWithImageRef(imageRef string) bool {
|
||||
for _, c := range f.ClientCalls {
|
||||
if c.ImageRef == imageRef {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WasCalledWithExtractCall is a helper method to confirm that the client was called with the given extract call
|
||||
// This matches exactly the calls made to the client in all fields
|
||||
func (f *FakeImageExtractor) WasCalledWithExtractCall(call ExtractCall) bool {
|
||||
for _, c := range f.ClientCalls {
|
||||
if c == call {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user