mirror of
https://github.com/kairos-io/kairos.git
synced 2025-02-09 05:18:51 +00:00
✨ Use kairos-agent from packages (#1354)
* ✨ Use kairos-agent from packages
Also drops all uneeded targets, code and so on for the agent
Moves profile-build into its own dir
Moves go.mod files into theyr own, one for testing and one for the
profile build
Adjusts earthly targets to the new stuff
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
* Bump repos and rework earthfile for kairos-agent package
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
* Restore missing earthly scripts
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
* Run apt update before apt install
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
* Copy go.{mod,sum} to local dir before tests
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
* Fix install tests
Is the only one not run in earthly, strange.
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
---------
Signed-off-by: Itxaka <itxaka.garcia@spectrocloud.com>
This commit is contained in:
parent
4c5e0e93e4
commit
5147429a0b
@ -1,294 +0,0 @@
|
||||
version: 2.1
|
||||
|
||||
jobs:
|
||||
|
||||
release:
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Release binary assets
|
||||
command: |
|
||||
curl https://luet.io/install.sh | sudo sh
|
||||
sudo luet repo add --type docker --yes --url quay.io/kairos/packages
|
||||
sudo luet install -y utils/goreleaser
|
||||
|
||||
build-arm:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
model:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
release:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build ARM images
|
||||
command: |
|
||||
mkdir -vp ~/.docker/cli-plugins/
|
||||
curl --silent -L "https://github.com/docker/buildx/releases/download/v0.5.1/buildx-v0.5.1.linux-amd64" > ~/.docker/cli-plugins/docker-buildx
|
||||
chmod a+x ~/.docker/cli-plugins/docker-buildx
|
||||
docker buildx version
|
||||
sudo apt-get update && sudo apt-get install -y binfmt-support qemu-user-static
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
docker run --privileged --rm tonistiigi/binfmt --install arm64
|
||||
docker context create buildcontext
|
||||
docker buildx create buildcontext --use
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
go get github.com/tcnksm/ghr
|
||||
echo $REGISTRY_PASSWORD | docker login -u $REGISTRY_USERNAME --password-stdin quay.io
|
||||
export TAG=${CIRCLE_TAG}
|
||||
else
|
||||
export TAG=latest
|
||||
fi
|
||||
|
||||
./earthly.sh +all-arm --IMAGE_NAME=kairos-<< parameters.flavor >>-$TAG.img --IMAGE=<< parameters.image >>-<< parameters.flavor >>:${TAG} --MODEL=<< parameters.model >> --FLAVOR=<< parameters.flavor >>
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
docker push << parameters.image >>-<< parameters.flavor >>:${TAG}
|
||||
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${TAG} ./build/
|
||||
fi
|
||||
run-test:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
test_suite:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +pull-release --FLAVOR=<< parameters.flavor >>
|
||||
ls -liah
|
||||
export ISO=$(ls build/*.iso)
|
||||
sudo mv $ISO build/kairos.iso
|
||||
./earthly.sh +run-qemu-test --FLAVOR=<< parameters.flavor >> --CONTAINER_IMAGE=ttl.sh/tests-<< parameters.flavor >>-$CIRCLE_SHA1:8h --TEST_SUITE=<< parameters.test_suite >>
|
||||
run-latest-test:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
test_suite:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +pull-release --FLAVOR=<< parameters.flavor >>
|
||||
ls -liah
|
||||
export ISO=$(ls build/*.iso)
|
||||
sudo mv $ISO build/kairos.iso
|
||||
./earthly.sh +run-qemu-test --FLAVOR=<< parameters.flavor >> --CONTAINER_IMAGE=ttl.sh/tests-<< parameters.flavor >>-$CIRCLE_SHA1:8h --TEST_SUITE=<< parameters.test_suite >>
|
||||
run-datasource-test:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
cloud_config:
|
||||
type: string
|
||||
test_suite:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +pull-build-artifacts --BUNDLE_IMAGE=ttl.sh/<< parameters.flavor >>-$CIRCLE_SHA1:8h
|
||||
ls -liah
|
||||
export ISO=$(ls build/*.iso)
|
||||
sudo mv $ISO build/kairos.iso
|
||||
./earthly.sh +datasource-iso --CLOUD_CONFIG=tests/assets/<< parameters.cloud_config >>.yaml
|
||||
./earthly.sh +run-qemu-datasource-tests --TEST_SUITE=<< parameters.test_suite >> --FLAVOR=<< parameters.flavor >>
|
||||
run-bundles-test:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +pull-build-artifacts --BUNDLE_IMAGE=ttl.sh/<< parameters.flavor >>-$CIRCLE_SHA1:8h
|
||||
ls -liah
|
||||
export ISO=$(ls build/*.iso)
|
||||
sudo mv $ISO build/kairos.iso
|
||||
./earthly.sh +prepare-bundles-tests
|
||||
./earthly.sh +run-qemu-bundles-tests --FLAVOR=<< parameters.flavor >>
|
||||
build-iso:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
release:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
go get github.com/tcnksm/ghr
|
||||
echo $REGISTRY_PASSWORD | docker login -u $REGISTRY_USERNAME --password-stdin quay.io
|
||||
export TAG=${CIRCLE_TAG}
|
||||
else
|
||||
export TAG=latest
|
||||
fi
|
||||
./earthly.sh +all --IMAGE=<< parameters.image >>-<< parameters.flavor >>:${TAG} --FLAVOR=<< parameters.flavor >> --ISO_NAME=kairos-<< parameters.flavor >>-${TAG}
|
||||
./earthly.sh +push-build-artifacts --BUNDLE_IMAGE=ttl.sh/<< parameters.flavor >>-$CIRCLE_SHA1:8h
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
docker push << parameters.image >>-<< parameters.flavor >>:${TAG}
|
||||
ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${TAG} ./build/
|
||||
fi
|
||||
docker tag << parameters.image >>-<< parameters.flavor >>:${TAG} ttl.sh/tests-<< parameters.flavor >>-$CIRCLE_SHA1:8h
|
||||
docker push ttl.sh/tests-<< parameters.flavor >>-$CIRCLE_SHA1:8h
|
||||
|
||||
lint:
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: medium
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +lint
|
||||
|
||||
build-bin:
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: medium
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +dist
|
||||
unit-test:
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: medium
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
./earthly.sh +test
|
||||
|
||||
build-framework:
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
image:
|
||||
type: string
|
||||
release:
|
||||
type: string
|
||||
machine:
|
||||
image: ubuntu-2004:current
|
||||
resource_class: large
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Build
|
||||
command: |
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
echo $REGISTRY_PASSWORD | docker login -u $REGISTRY_USERNAME --password-stdin quay.io
|
||||
export TAG=${CIRCLE_TAG}
|
||||
else
|
||||
export TAG=latest
|
||||
fi
|
||||
./earthly.sh +framework-image --IMG=<< parameters.image >>:${TAG} --FLAVOR=<< parameters.flavor >> --WITH_KERNEL=false
|
||||
./earthly.sh +framework-image --IMG=<< parameters.image >>-generic:${TAG} --FLAVOR=<< parameters.flavor >> --WITH_KERNEL=true
|
||||
if [ "<< parameters.release >>" == "yes" ]; then
|
||||
docker push << parameters.image >>-<< parameters.flavor >>:${TAG}
|
||||
fi
|
||||
|
||||
workflows:
|
||||
|
||||
build:
|
||||
jobs:
|
||||
- build-arm:
|
||||
matrix:
|
||||
parameters:
|
||||
image: ["quay.io/kairos/core"]
|
||||
flavor: ["opensuse-tumbleweed-arm-rpi", "opensuse-leap-arm-rpi", "alpine-arm-rpi"]
|
||||
model: ["rpi64"]
|
||||
release: ["no"]
|
||||
- build-framework:
|
||||
matrix:
|
||||
parameters:
|
||||
image: ["quay.io/kairos/framework"]
|
||||
flavor: ["opensuse-leap"]
|
||||
release: ["no"]
|
||||
# - run-datasource-test:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# cloud_config: ["autoinstall"]
|
||||
# flavor: ["alpine","opensuse","ubuntu"]
|
||||
# test_suite: ["reset-test", "autoinstall-test"]
|
||||
# requires:
|
||||
# - build-iso
|
||||
# - run-latest-test:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# flavor: ["alpine","opensuse","ubuntu"]
|
||||
# test_suite: ["upgrade-latest-with-cli"]
|
||||
# requires:
|
||||
# - build-iso
|
||||
# - run-test:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# flavor: ["alpine","opensuse","ubuntu"]
|
||||
# test_suite: ["upgrade-with-cli"]
|
||||
# requires:
|
||||
# - build-iso
|
||||
# - run-bundles-test:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# flavor: ["opensuse"]
|
||||
# requires:
|
||||
# - build-iso
|
||||
# - build-iso:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# image: [quay.io/kairos/core]
|
||||
# flavor: ["alpine","opensuse","ubuntu", "rockylinux", "fedora"]
|
||||
# release: ["no"]
|
||||
# - run-datasource-test:
|
||||
# matrix:
|
||||
# parameters:
|
||||
# cloud_config: ["autoinstall"]
|
||||
# flavor: ["fedora", "rockylinux"]
|
||||
# test_suite: ["reset-test", "autoinstall-test"]
|
||||
# requires:
|
||||
# - build-iso
|
||||
lint:
|
||||
jobs:
|
||||
- lint
|
||||
test:
|
||||
jobs:
|
||||
- unit-test
|
||||
- build-bin
|
||||
|
3
.github/workflows/image.yaml
vendored
3
.github/workflows/image.yaml
vendored
@ -252,7 +252,8 @@ jobs:
|
||||
export PATH=$PATH:$GOPATH/bin
|
||||
export CREATE_VM=true
|
||||
export FLAVOR=${{ matrix.flavor }}
|
||||
go run github.com/onsi/ginkgo/v2/ginkgo --label-filter "install-test" --fail-fast -r ./tests/
|
||||
cd tests
|
||||
go run github.com/onsi/ginkgo/v2/ginkgo --label-filter "install-test" --fail-fast -r ./...
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
|
28
.github/workflows/release_bin.yaml
vendored
28
.github/workflows/release_bin.yaml
vendored
@ -1,28 +0,0 @@
|
||||
name: goreleaser
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: |
|
||||
git fetch --prune --unshallow
|
||||
- name: Generate version
|
||||
run: echo "VERSION=$(git describe --always --tags --dirty)" >> $GITHUB_ENV
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.20
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
version: latest
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
45
.github/workflows/unit-tests.yml
vendored
45
.github/workflows/unit-tests.yml
vendored
@ -1,45 +0,0 @@
|
||||
name: Unit tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '!docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
jobs:
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ^1.20
|
||||
- name: Install earthly
|
||||
uses: Luet-lab/luet-install-action@v1
|
||||
with:
|
||||
repository: quay.io/kairos/packages
|
||||
packages: utils/earthly
|
||||
- name: Run Build
|
||||
run: |
|
||||
earthly +dist
|
||||
- name: Run tests
|
||||
run: |
|
||||
earthly +test
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./coverage.out
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: build.zip
|
||||
path: |
|
||||
dist/*
|
29
.github/workflows/webui.yaml
vendored
29
.github/workflows/webui.yaml
vendored
@ -1,29 +0,0 @@
|
||||
name: WebUI tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ci-webui-${{ github.head_ref || github.ref }}-${{ github.repository }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
webui:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install earthly
|
||||
uses: Luet-lab/luet-install-action@v1
|
||||
with:
|
||||
repository: quay.io/kairos/packages
|
||||
packages: utils/earthly
|
||||
- name: WebUI tests
|
||||
run: earthly +webui-tests
|
@ -1,23 +0,0 @@
|
||||
run:
|
||||
timeout: 5m
|
||||
tests: false
|
||||
linters:
|
||||
enable:
|
||||
- revive # replacement for golint
|
||||
- dupl # check duplicated code
|
||||
- goconst # check strings that can turn into constants
|
||||
- gofmt # check fmt
|
||||
- goheader # Check license headers, only checks files in current year
|
||||
- goimports # check imports
|
||||
- gocyclo # check complexity
|
||||
- govet
|
||||
- gosimple
|
||||
- deadcode
|
||||
- ineffassign
|
||||
- unused
|
||||
- varcheck
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- structcheck
|
||||
- godot
|
||||
- misspell
|
@ -1,39 +0,0 @@
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
project_name: kairos-agent
|
||||
builds:
|
||||
- ldflags:
|
||||
- -w -s -X "github.com/kairos-io/kairos/v2/internal/common.VERSION={{.Env.VERSION}}"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- 386
|
||||
main: ./cmd/agent/
|
||||
binary: '{{ .ProjectName }}'
|
||||
source:
|
||||
enabled: true
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-source'
|
||||
archives:
|
||||
# Default template uses underscores instead of -
|
||||
- name_template: "{{ .ProjectName }}-{{ .Tag }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
# TODO: this is deprecated -> https://goreleaser.com/deprecations#archivesreplacements
|
||||
replacements:
|
||||
darwin: Darwin
|
||||
linux: Linux
|
||||
windows: Windows
|
||||
386: i386
|
||||
amd64: x86_64
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}-{{ .Tag }}-checksums.txt'
|
||||
snapshot:
|
||||
name_template: "{{ .Tag }}-next"
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^Merge pull request'
|
179
Earthfile
179
Earthfile
@ -8,7 +8,6 @@ ARG ISO_NAME=kairos-${VARIANT}-${FLAVOR}
|
||||
# renovate: datasource=docker depName=quay.io/luet/base
|
||||
ARG LUET_VERSION=0.34.0
|
||||
ARG OS_ID=kairos
|
||||
ARG REPOSITORIES_FILE=framework-profile.yaml
|
||||
# renovate: datasource=docker depName=aquasec/trivy
|
||||
ARG TRIVY_VERSION=0.40.0
|
||||
ARG COSIGN_SKIP=".*quay.io/kairos/.*"
|
||||
@ -56,23 +55,14 @@ all-arm-generic:
|
||||
BUILD --platform=linux/arm64 +image
|
||||
BUILD --platform=linux/arm64 +iso
|
||||
|
||||
go-deps:
|
||||
go-deps-test:
|
||||
ARG GO_VERSION
|
||||
FROM golang:$GO_VERSION
|
||||
WORKDIR /build
|
||||
COPY go.mod go.sum ./
|
||||
COPY tests/go.mod tests/go.sum ./
|
||||
RUN go mod download
|
||||
RUN apt-get update && apt-get install -y upx
|
||||
SAVE ARTIFACT go.mod AS LOCAL go.mod
|
||||
SAVE ARTIFACT go.sum AS LOCAL go.sum
|
||||
|
||||
test:
|
||||
FROM +go-deps
|
||||
WORKDIR /build
|
||||
COPY +luet/luet /usr/bin/luet
|
||||
COPY . .
|
||||
RUN go run github.com/onsi/ginkgo/v2/ginkgo --fail-fast --covermode=atomic --coverprofile=coverage.out -p -r ./pkg ./internal ./cmd ./sdk
|
||||
SAVE ARTIFACT coverage.out AS LOCAL coverage.out
|
||||
SAVE ARTIFACT go.mod go.mod AS LOCAL go.mod
|
||||
SAVE ARTIFACT go.sum go.sum AS LOCAL go.sum
|
||||
|
||||
OSRELEASE:
|
||||
COMMAND
|
||||
@ -90,22 +80,6 @@ OSRELEASE:
|
||||
# update OS-release file
|
||||
RUN envsubst >>/etc/os-release </usr/lib/os-release.tmpl
|
||||
|
||||
BUILD_GOLANG:
|
||||
COMMAND
|
||||
WORKDIR /build
|
||||
COPY . ./
|
||||
ARG CGO_ENABLED
|
||||
ARG BIN
|
||||
ARG SRC
|
||||
ARG VERSION
|
||||
|
||||
ENV CGO_ENABLED=${CGO_ENABLED}
|
||||
ARG LDFLAGS="-s -w -X 'github.com/kairos-io/kairos/v2/internal/common.VERSION=${VERSION}'"
|
||||
RUN --no-cache echo "Building ${BIN} from ${SRC} using ${VERSION}"
|
||||
RUN echo ${LDFLAGS}
|
||||
RUN go build -o ${BIN} -ldflags "${LDFLAGS}" ./cmd/${SRC} && upx ${BIN}
|
||||
SAVE ARTIFACT ${BIN} ${BIN} AS LOCAL build/${BIN}
|
||||
|
||||
uuidgen:
|
||||
FROM alpine
|
||||
RUN apk add uuidgen
|
||||
@ -127,42 +101,6 @@ version:
|
||||
ARG VERSION=$(cat VERSION)
|
||||
SAVE ARTIFACT VERSION VERSION
|
||||
|
||||
|
||||
build-kairos-agent:
|
||||
FROM +go-deps
|
||||
COPY +webui-deps/node_modules ./internal/webui/public/node_modules
|
||||
COPY +docs/public/local ./internal/webui/public/local
|
||||
COPY +version/VERSION ./
|
||||
ARG VERSION=$(cat VERSION)
|
||||
RUN echo $(cat VERSION)
|
||||
DO +BUILD_GOLANG --BIN=kairos-agent --SRC=agent --CGO_ENABLED=$CGO_ENABLED --VERSION=$VERSION
|
||||
|
||||
build:
|
||||
BUILD +build-kairos-agent
|
||||
|
||||
dist:
|
||||
ARG GO_VERSION
|
||||
FROM golang:$GO_VERSION
|
||||
COPY +luet/luet /usr/bin/luet
|
||||
RUN mkdir -p /etc/luet/repos.conf.d/
|
||||
RUN luet repo add kairos --yes --url quay.io/kairos/packages --type docker
|
||||
RUN luet install -y utils/goreleaser
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
COPY +version/VERSION ./
|
||||
RUN echo $(cat VERSION)
|
||||
RUN VERSION=$(cat VERSION) goreleaser build --rm-dist --skip-validate --snapshot
|
||||
SAVE ARTIFACT /build/dist/* AS LOCAL dist/
|
||||
|
||||
golint:
|
||||
ARG GO_VERSION
|
||||
FROM golang:$GO_VERSION
|
||||
ARG GOLINT_VERSION
|
||||
RUN wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v$GOLINT_VERSION
|
||||
WORKDIR /build
|
||||
COPY . .
|
||||
RUN golangci-lint run
|
||||
|
||||
hadolint:
|
||||
ARG HADOLINT_VERSION
|
||||
FROM hadolint/hadolint:$HADOLINT_VERSION
|
||||
@ -191,7 +129,6 @@ yamllint:
|
||||
RUN yamllint .github/workflows/ overlay/
|
||||
|
||||
lint:
|
||||
BUILD +golint
|
||||
BUILD +hadolint
|
||||
BUILD +renovate-validate
|
||||
BUILD +shellcheck-lint
|
||||
@ -222,39 +159,36 @@ luet:
|
||||
### Image Build targets
|
||||
###
|
||||
|
||||
framework:
|
||||
ARG COSIGN_SKIP
|
||||
ARG REPOSITORIES_FILE
|
||||
ARG COSIGN_EXPERIMENTAL
|
||||
ARG COSIGN_REPOSITORY
|
||||
ARG FLAVOR
|
||||
ARG VERSION
|
||||
ARG LDFLAGS="-s -w -X 'github.com/kairos-io/kairos/v2/internal/common.VERSION=$VERSION'"
|
||||
|
||||
# This generates the framework base by installing luet packages generated with the profile-build + framework-profile.yaml
|
||||
# file
|
||||
# Installs everything under the /framework dir and saves that as an artifact
|
||||
framework-luet:
|
||||
FROM golang:alpine
|
||||
ARG FLAVOR
|
||||
WORKDIR /build
|
||||
COPY ./profile-build /build
|
||||
COPY framework-profile.yaml /build
|
||||
COPY +luet/luet /usr/bin/luet
|
||||
|
||||
# cosign keyless verify
|
||||
ENV COSIGN_EXPERIMENTAL=${COSIGN_EXPERIMENTAL}
|
||||
# Repo containing signatures
|
||||
ENV COSIGN_REPOSITORY=${COSIGN_REPOSITORY}
|
||||
# Skip this repo artifacts verify as they are not signed
|
||||
ENV COSIGN_SKIP=${COSIGN_SKIP}
|
||||
|
||||
ENV USER=root
|
||||
|
||||
COPY . /build
|
||||
|
||||
RUN go run -ldflags "${LDFLAGS}" ./cmd/profile-build/main.go ${FLAVOR} $REPOSITORIES_FILE /framework
|
||||
|
||||
# Copy kairos binaries
|
||||
COPY +build-kairos-agent/kairos-agent /framework/usr/bin/kairos-agent
|
||||
COPY +luet/luet /framework/usr/bin/luet
|
||||
|
||||
RUN go run main.go ${FLAVOR} framework-profile.yaml /framework
|
||||
RUN luet cleanup --system-target /framework
|
||||
# COPY luet into the final framework
|
||||
# TODO: Understand why?
|
||||
COPY +luet/luet /framework/usr/bin/luet
|
||||
# more cleanup
|
||||
RUN rm -rf /framework/var/luet
|
||||
RUN rm -rf /framework/var/cache
|
||||
|
||||
SAVE ARTIFACT --keep-own /framework framework-luet
|
||||
|
||||
framework:
|
||||
FROM alpine
|
||||
ARG FLAVOR
|
||||
# This ARG does nothing?
|
||||
ARG VERSION
|
||||
COPY +framework-luet/framework-luet /framework
|
||||
|
||||
# Copy overlay files
|
||||
# TODO: Make this also a package?
|
||||
COPY overlay/files /framework
|
||||
# Copy flavor-specific overlay files
|
||||
IF [ "$FLAVOR" = "alpine-opensuse-leap" ] || [ "$FLAVOR" = "alpine-ubuntu" ]
|
||||
@ -272,8 +206,6 @@ framework:
|
||||
COPY overlay/files-ubuntu/ /framework
|
||||
END
|
||||
|
||||
RUN rm -rf /framework/var/luet
|
||||
RUN rm -rf /framework/var/cache
|
||||
SAVE ARTIFACT --keep-own /framework/ framework
|
||||
|
||||
build-framework-image:
|
||||
@ -574,30 +506,14 @@ grype-scan:
|
||||
SAVE ARTIFACT /build/report.sarif report.sarif AS LOCAL build/${VARIANT}-${FLAVOR}-${VERSION}-grype.sarif
|
||||
SAVE ARTIFACT /build/report.json report.json AS LOCAL build/${VARIANT}-${FLAVOR}-${VERSION}-grype.json
|
||||
|
||||
linux-bench:
|
||||
ARG GO_VERSION
|
||||
FROM golang:$GO_VERSION
|
||||
GIT CLONE https://github.com/aquasecurity/linux-bench /linux-bench-src
|
||||
RUN cd /linux-bench-src && CGO_ENABLED=0 go build -o linux-bench . && mv linux-bench /
|
||||
SAVE ARTIFACT /linux-bench /linux-bench
|
||||
|
||||
# The target below should run on a live host instead.
|
||||
# However, some checks are relevant as well at container level.
|
||||
# It is good enough for a quick assessment.
|
||||
linux-bench-scan:
|
||||
FROM +image
|
||||
GIT CLONE https://github.com/aquasecurity/linux-bench /build/linux-bench
|
||||
WORKDIR /build/linux-bench
|
||||
COPY +linux-bench/linux-bench /build/linux-bench/linux-bench
|
||||
RUN /build/linux-bench/linux-bench
|
||||
|
||||
|
||||
###
|
||||
### Test targets
|
||||
###
|
||||
# usage e.g. ./earthly.sh +run-qemu-datasource-tests --FLAVOR=alpine-opensuse-leap --FROM_ARTIFACTS=true
|
||||
run-qemu-datasource-tests:
|
||||
FROM +go-deps
|
||||
FROM +go-deps-test
|
||||
RUN apt update
|
||||
RUN apt install -y qemu-system-x86 qemu-utils golang git
|
||||
WORKDIR /test
|
||||
ARG FLAVOR
|
||||
@ -627,12 +543,13 @@ run-qemu-datasource-tests:
|
||||
ENV DATASOURCE=/test/build/datasource.iso
|
||||
END
|
||||
ENV CLOUD_INIT=/tests/tests/$CLOUD_CONFIG
|
||||
|
||||
COPY +go-deps-test/go.mod go.mod
|
||||
COPY +go-deps-test/go.sum go.sum
|
||||
RUN go run github.com/onsi/ginkgo/v2/ginkgo -v --label-filter "$TEST_SUITE" --fail-fast -r ./tests/
|
||||
|
||||
|
||||
run-qemu-netboot-test:
|
||||
FROM +go-deps
|
||||
FROM +go-deps-test
|
||||
COPY . /test
|
||||
WORKDIR /test
|
||||
|
||||
@ -658,7 +575,8 @@ run-qemu-netboot-test:
|
||||
ENV USE_QEMU=true
|
||||
ARG TEST_SUITE=netboot-test
|
||||
|
||||
|
||||
COPY +go-deps-test/go.mod go.mod
|
||||
COPY +go-deps-test/go.sum go.sum
|
||||
# TODO: use --pull or something to cache the python image in Earthly
|
||||
WITH DOCKER
|
||||
RUN docker run -d -v $PWD/build:/build --workdir=/build \
|
||||
@ -667,7 +585,8 @@ run-qemu-netboot-test:
|
||||
END
|
||||
|
||||
run-qemu-test:
|
||||
FROM +go-deps
|
||||
FROM +go-deps-test
|
||||
RUN apt update
|
||||
RUN apt install -y qemu-system-x86 qemu-utils git && apt clean
|
||||
ARG FLAVOR
|
||||
ARG TEST_SUITE=upgrade-with-cli
|
||||
@ -686,6 +605,8 @@ run-qemu-test:
|
||||
COPY +iso/kairos.iso kairos.iso
|
||||
ENV ISO=/build/kairos.iso
|
||||
END
|
||||
COPY +go-deps-test/go.mod go.mod
|
||||
COPY +go-deps-test/go.sum go.sum
|
||||
RUN go run github.com/onsi/ginkgo/v2/ginkgo -v --label-filter "$TEST_SUITE" --fail-fast -r ./tests/
|
||||
|
||||
###
|
||||
@ -778,27 +699,12 @@ examples-bundle-config:
|
||||
RUN envsubst >> tests/assets/live-overlay.yaml < tests/assets/live-overlay.tmpl
|
||||
SAVE ARTIFACT tests/assets/live-overlay.yaml AS LOCAL bundles-config.yaml
|
||||
|
||||
webui-deps:
|
||||
FROM node:19-alpine
|
||||
COPY . .
|
||||
WORKDIR ./internal/webui/public
|
||||
RUN npm install
|
||||
SAVE ARTIFACT node_modules /node_modules AS LOCAL internal/webui/public/node_modules
|
||||
|
||||
webui-tests:
|
||||
FROM ubuntu:22.10
|
||||
RUN apt-get update && apt-get install -y libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb golang nodejs npm
|
||||
COPY +build-kairos-agent/kairos-agent /usr/bin/kairos-agent
|
||||
COPY . src/
|
||||
WORKDIR src/
|
||||
RUN .github/cypress_tests.sh
|
||||
SAVE ARTIFACT /src/internal/webui/public/cypress/videos videos
|
||||
|
||||
docs:
|
||||
FROM node:19-bullseye
|
||||
ARG TARGETARCH
|
||||
|
||||
# Install dependencies
|
||||
RUN apt update
|
||||
RUN apt install git
|
||||
# renovate: datasource=github-releases depName=gohugoio/hugo
|
||||
ARG HUGO_VERSION="0.110.0"
|
||||
@ -847,7 +753,10 @@ generate-schema:
|
||||
FROM alpine
|
||||
COPY . ./
|
||||
COPY +version/VERSION ./
|
||||
COPY +build-kairos-agent/kairos-agent /usr/bin/kairos-agent
|
||||
COPY +luet/luet /usr/bin/luet
|
||||
RUN mkdir -p /etc/luet/repos.conf.d/
|
||||
RUN luet repo add kairos --yes --url quay.io/kairos/packages --type docker
|
||||
RUN luet install -y system/kairos-agent
|
||||
ARG RELEASE_VERSION=$(cat VERSION)
|
||||
RUN mkdir "docs/static/$RELEASE_VERSION"
|
||||
ARG SCHEMA_FILE="docs/static/$RELEASE_VERSION/cloud-config.json"
|
||||
|
@ -1,517 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/agent"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/webui"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/state"
|
||||
"github.com/kairos-io/kairos/v2/internal/common"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/urfave/cli/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var configScanDir = []string{"/oem", "/usr/local/cloud-config", "/run/initramfs/live"}
|
||||
|
||||
// ReleasesToOutput gets a semver.Collection and outputs it in the given format
|
||||
// Only used here.
|
||||
func ReleasesToOutput(rels semver.Collection, output string) []string {
|
||||
// Set them back to their original version number with the v in front
|
||||
var stringRels []string
|
||||
for _, v := range rels {
|
||||
stringRels = append(stringRels, v.Original())
|
||||
}
|
||||
switch strings.ToLower(output) {
|
||||
case "yaml":
|
||||
d, _ := yaml.Marshal(stringRels)
|
||||
return []string{string(d)}
|
||||
case "json":
|
||||
d, _ := json.Marshal(stringRels)
|
||||
return []string{string(d)}
|
||||
default:
|
||||
return stringRels
|
||||
}
|
||||
}
|
||||
|
||||
var cmds = []*cli.Command{
|
||||
{
|
||||
Name: "upgrade",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "Force an upgrade",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "Show debug output",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "image",
|
||||
Usage: "Specify an full image reference, e.g.: quay.io/some/image:tag",
|
||||
},
|
||||
&cli.StringFlag{Name: "auth-username", Usage: "Username to authenticate to registry"},
|
||||
&cli.StringFlag{Name: "auth-password", Usage: "Password to authenticate to registry"},
|
||||
&cli.StringFlag{Name: "auth-server-address", Usage: "Authentication server address"},
|
||||
&cli.StringFlag{Name: "auth-type", Usage: "Auth type"},
|
||||
&cli.StringFlag{Name: "auth-registry-token", Usage: "Authentication registry token"},
|
||||
&cli.StringFlag{Name: "auth-identity-token", Usage: "Authentication identity token"},
|
||||
&cli.BoolFlag{Name: "pre", Usage: "Include pre-releases (rc, beta, alpha)"},
|
||||
},
|
||||
Description: `
|
||||
Manually upgrade a kairos node.
|
||||
|
||||
By default takes no arguments, defaulting to latest available release, to specify a version, pass it as argument:
|
||||
|
||||
$ kairos upgrade v1.20....
|
||||
|
||||
To retrieve all the available versions, use "kairos upgrade list-releases"
|
||||
|
||||
$ kairos upgrade list-releases
|
||||
|
||||
See https://kairos.io/docs/upgrade/manual/ for documentation.
|
||||
|
||||
`,
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Output format (json|yaml|terminal)",
|
||||
},
|
||||
&cli.BoolFlag{Name: "pre", Usage: "Include pre-releases (rc, beta, alpha)"},
|
||||
},
|
||||
Name: "list-releases",
|
||||
Description: `List all available releases versions`,
|
||||
Action: func(c *cli.Context) error {
|
||||
releases := agent.ListReleases(c.Bool("pre"))
|
||||
list := ReleasesToOutput(releases, c.String("output"))
|
||||
for _, i := range list {
|
||||
fmt.Println(i)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Action: func(c *cli.Context) error {
|
||||
var v string
|
||||
if c.Args().Len() == 1 {
|
||||
v = c.Args().First()
|
||||
}
|
||||
|
||||
return agent.Upgrade(
|
||||
v, c.String("image"), c.Bool("force"), c.Bool("debug"),
|
||||
c.Bool("strict-validation"), configScanDir,
|
||||
c.String("auth-username"), c.String("auth-password"), c.String("auth-server-address"),
|
||||
c.String("auth-type"), c.String("auth-registry-token"), c.String("auth-identity-token"),
|
||||
c.Bool("pre"),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "notify",
|
||||
Usage: "notify <event> <config dir>...",
|
||||
UsageText: "emits the given event with a generic event payload",
|
||||
Description: `
|
||||
Sends a generic event payload with the configuration found in the scanned directories.
|
||||
`,
|
||||
Aliases: []string{},
|
||||
Flags: []cli.Flag{},
|
||||
Action: func(c *cli.Context) error {
|
||||
dirs := []string{"/oem", "/usr/local/cloud-config"}
|
||||
if c.Args().Len() > 1 {
|
||||
dirs = c.Args().Slice()[1:]
|
||||
}
|
||||
|
||||
return agent.Notify(c.Args().First(), dirs)
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "start",
|
||||
Usage: "Starts the kairos agent",
|
||||
UsageText: "starts the agent",
|
||||
Description: `
|
||||
Starts the kairos agent which automatically bootstrap and advertize to the kairos network.
|
||||
`,
|
||||
Aliases: []string{"s"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "restart",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "force",
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "api",
|
||||
Value: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
dirs := []string{"/oem", "/usr/local/cloud-config"}
|
||||
if c.Args().Present() {
|
||||
dirs = c.Args().Slice()
|
||||
}
|
||||
|
||||
opts := []agent.Option{
|
||||
agent.WithAPI(c.String("api")),
|
||||
agent.WithDirectory(dirs...),
|
||||
}
|
||||
|
||||
if c.Bool("force") {
|
||||
opts = append(opts, agent.ForceAgent)
|
||||
}
|
||||
|
||||
if c.Bool("restart") {
|
||||
opts = append(opts, agent.RestartAgent)
|
||||
}
|
||||
|
||||
return agent.Run(opts...)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "install-bundle",
|
||||
Usage: "Installs a kairos bundle",
|
||||
Description: `
|
||||
|
||||
Manually installs a kairos bundle.
|
||||
|
||||
E.g. kairos-agent install-bundle container:quay.io/kairos/kairos...
|
||||
|
||||
`,
|
||||
Aliases: []string{"i"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "repository",
|
||||
EnvVars: []string{"REPOSITORY"},
|
||||
Value: "docker://quay.io/kairos/packages",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "local-file",
|
||||
EnvVars: []string{"LOCAL_FILE"},
|
||||
},
|
||||
},
|
||||
UsageText: "Install a bundle manually in the node",
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Args().Len() != 1 {
|
||||
return fmt.Errorf("bundle name required")
|
||||
}
|
||||
|
||||
return bundles.RunBundles([]bundles.BundleOption{bundles.WithRepository(c.String("repository")), bundles.WithTarget(c.Args().First()), bundles.WithLocalFile(c.Bool("local-file"))})
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "uuid",
|
||||
Usage: "Prints the local UUID",
|
||||
Description: "Print node uuid",
|
||||
Aliases: []string{"u"},
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Print(machine.UUID())
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "webui",
|
||||
Usage: "Starts the webui",
|
||||
Description: "Starts the webui installer",
|
||||
Aliases: []string{"w"},
|
||||
Action: func(c *cli.Context) error {
|
||||
return webui.Start(context.Background())
|
||||
//return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Usage: "get machine configuration",
|
||||
Description: "Print machine state information, e.g. `state get uuid` returns the machine uuid",
|
||||
Aliases: []string{"c"},
|
||||
Action: func(c *cli.Context) error {
|
||||
runtime, err := state.NewRuntime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(runtime)
|
||||
return err
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "show",
|
||||
Usage: "Shows the machine configuration",
|
||||
Description: "Show the runtime configuration of the machine. It will scan the machine for all the configuration and will return the config file processed and found.",
|
||||
Aliases: []string{"s"},
|
||||
Action: func(c *cli.Context) error {
|
||||
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configStr, err := config.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s", configStr)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "Get specific data from the configuration",
|
||||
UsageText: `
|
||||
Use it to retrieve configuration programmatically from the CLI:
|
||||
|
||||
$ kairos-agent config get k3s.enabled
|
||||
true
|
||||
|
||||
or
|
||||
|
||||
$ kairos-agent config get k3s
|
||||
enabled: true`,
|
||||
Description: "It allows to navigate the YAML config file by searching with 'yq' style keywords as `config get k3s` to retrieve the k3s config block",
|
||||
Aliases: []string{"g"},
|
||||
Action: func(c *cli.Context) error {
|
||||
config, err := config.Scan(collector.Directories(configScanDir...), collector.NoLogs, collector.StrictValidation(c.Bool("strict-validation")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := config.Query(c.Args().First())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("%s", res)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "state",
|
||||
Usage: "get machine state",
|
||||
Description: "Print machine state information, e.g. `state get uuid` returns the machine uuid",
|
||||
Aliases: []string{"s"},
|
||||
Action: func(c *cli.Context) error {
|
||||
runtime, err := state.NewRuntime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(runtime)
|
||||
return err
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
{
|
||||
Name: "apply",
|
||||
Usage: "Applies a machine state",
|
||||
Description: "Applies machine configuration in runtimes",
|
||||
Aliases: []string{"a"},
|
||||
Action: func(c *cli.Context) error {
|
||||
// TODO
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "get specific ",
|
||||
Description: "query state data",
|
||||
Aliases: []string{"g"},
|
||||
Action: func(c *cli.Context) error {
|
||||
runtime, err := state.NewRuntime()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := runtime.Query(c.Args().First())
|
||||
fmt.Print(res)
|
||||
return err
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "interactive-install",
|
||||
Description: `
|
||||
Starts kairos in interactive mode install.
|
||||
|
||||
It will ask prompt for several questions and perform an install depending on the providers available in the system.
|
||||
|
||||
See also https://kairos.io/installation/interactive_install/ for documentation.
|
||||
|
||||
This command is meant to be used from the boot GRUB menu, but can be also started manually`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "shell",
|
||||
},
|
||||
},
|
||||
Usage: "Starts interactive installation",
|
||||
Action: func(c *cli.Context) error {
|
||||
return agent.InteractiveInstall(c.Bool("shell"))
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "manual-install",
|
||||
Usage: "Starts the manual installation",
|
||||
Description: `
|
||||
`,
|
||||
Aliases: []string{"m"},
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "device",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "poweroff",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "reboot",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.NArg() == 0 {
|
||||
return fmt.Errorf("expect one argument. the config file - if you don't have it, use the interactive-install")
|
||||
}
|
||||
config := c.Args().First()
|
||||
|
||||
options := map[string]string{"device": c.String("device")}
|
||||
|
||||
if c.Bool("poweroff") {
|
||||
options["poweroff"] = "true"
|
||||
}
|
||||
|
||||
if c.Bool("reboot") {
|
||||
options["reboot"] = "true"
|
||||
}
|
||||
|
||||
return agent.ManualInstall(config, options, c.Bool("strict-validation"))
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Starts the kairos pairing installation",
|
||||
Description: `
|
||||
Starts kairos in pairing mode.
|
||||
|
||||
It will print out a QR code which can be used with "kairos register" to send over a configuration and bootstraping a kairos node.
|
||||
|
||||
See also https://kairos.io/docs/installation/qrcode/ for documentation.
|
||||
|
||||
This command is meant to be used from the boot GRUB menu, but can be started manually`,
|
||||
Aliases: []string{"i"},
|
||||
Action: func(c *cli.Context) error {
|
||||
return agent.Install(configScanDir...)
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "recovery",
|
||||
Aliases: []string{"r"},
|
||||
Action: func(c *cli.Context) error {
|
||||
return agent.Recovery()
|
||||
},
|
||||
Usage: "Starts kairos recovery mode",
|
||||
Description: `
|
||||
Starts kairos recovery mode.
|
||||
|
||||
In recovery mode a QR code will be printed out on the screen which should be used in conjunction with "kairos bridge". Pass by the QR code as snapshot
|
||||
to the bridge to connect over the machine which runs the "kairos recovery" command.
|
||||
|
||||
See also https://kairos.io/after_install/recovery_mode/ for documentation.
|
||||
|
||||
This command is meant to be used from the boot GRUB menu, but can likely be used standalone`,
|
||||
},
|
||||
|
||||
{
|
||||
Name: "reset",
|
||||
Action: func(c *cli.Context) error {
|
||||
return agent.Reset(configScanDir...)
|
||||
},
|
||||
Usage: "Starts kairos reset mode",
|
||||
Description: `
|
||||
Starts kairos reset mode, it will nuke completely the node data and restart fresh.
|
||||
Attention ! this will delete any persistent data on the node. It is equivalent to re-init the node right after the installation.
|
||||
|
||||
In reset mode a the node will automatically reset
|
||||
|
||||
See also https://kairos.io/after_install/reset_mode/ for documentation.
|
||||
|
||||
This command is meant to be used from the boot GRUB menu, but can likely be used standalone`,
|
||||
},
|
||||
{
|
||||
Name: "validate",
|
||||
Action: func(c *cli.Context) error {
|
||||
config := c.Args().First()
|
||||
return agent.Validate(config)
|
||||
},
|
||||
Usage: "Validates a cloud config file",
|
||||
Description: `
|
||||
The validate command expects a configuration file as its only argument. Local files and URLs are accepted.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Name: "print-schema",
|
||||
Action: func(c *cli.Context) error {
|
||||
|
||||
json, err := agent.JSONSchema(common.VERSION)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(json)
|
||||
|
||||
return nil
|
||||
},
|
||||
Usage: "Print out Kairos' Cloud Configuration JSON Schema",
|
||||
Description: `Prints out Kairos' Cloud Configuration JSON Schema`,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
app := &cli.App{
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "strict-validation",
|
||||
Usage: "Fail instead of warn on validation errors.",
|
||||
EnvVars: []string{"STRICT_VALIDATIONS"},
|
||||
},
|
||||
},
|
||||
Name: "kairos-agent",
|
||||
Version: common.VERSION,
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "Ettore Di Giacinto",
|
||||
},
|
||||
},
|
||||
Usage: "kairos agent start",
|
||||
Description: `
|
||||
The kairos agent is a component to abstract away node ops, providing a common feature-set across kairos variants.
|
||||
`,
|
||||
UsageText: ``,
|
||||
Copyright: "kairos authors",
|
||||
|
||||
Commands: cmds,
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/profile"
|
||||
"github.com/kairos-io/kairos/v2/internal/common"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
app := &cli.App{
|
||||
Name: "profile-build",
|
||||
Version: common.VERSION,
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "Kairos authors",
|
||||
},
|
||||
},
|
||||
Usage: "Build kairos framework images",
|
||||
Description: `
|
||||
Uses profile files to build kairos images`,
|
||||
UsageText: ``,
|
||||
Copyright: "kairos authors",
|
||||
ArgsUsage: "flavor profileName profileFile outputDirectory",
|
||||
Action: func(c *cli.Context) error {
|
||||
return profile.BuildFlavor(c.Args().Get(0), c.Args().Get(1), c.Args().Get(2))
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock --rm -t -v ${pwd}:/workspace -v earthly-tmp:/tmp/earthly:rw earthly/earthly:v0.7.4 --allow-privileged @args
|
||||
docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock --rm -t -v ${pwd}:/workspace -v earthly-tmp:/tmp/earthly:rw earthly/earthly:v0.7.4 --allow-privileged @args
|
||||
|
@ -7,6 +7,7 @@ common:
|
||||
- system/grub2-efi
|
||||
- system/elemental-cli
|
||||
- system/immucore
|
||||
- system/kairos-agent
|
||||
flavors:
|
||||
debian:
|
||||
- systemd-base
|
||||
@ -87,9 +88,9 @@ repositories:
|
||||
priority: 2
|
||||
urls:
|
||||
- "quay.io/kairos/packages"
|
||||
reference: 20230420084320-repository.yaml
|
||||
reference: 20230425110728-repository.yaml
|
||||
- !!merge <<: *kairos
|
||||
arch: arm64
|
||||
urls:
|
||||
- "quay.io/kairos/packages-arm64"
|
||||
reference: 20230420090242-repository.yaml
|
||||
reference: 20230425111926-repository.yaml
|
||||
|
124
go.mod
124
go.mod
@ -1,124 +0,0 @@
|
||||
module github.com/kairos-io/kairos/v2
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/avast/retry-go v3.0.0+incompatible
|
||||
github.com/erikgeiser/promptkit v0.8.0
|
||||
github.com/google/go-github/v40 v40.0.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/itchyny/gojq v0.12.12
|
||||
github.com/jaypipes/ghw v0.10.0
|
||||
github.com/kairos-io/kairos-sdk v0.0.2-0.20230414094028-0c9d2bd9e6ae
|
||||
github.com/kairos-io/kcrypt v0.5.2
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb
|
||||
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5
|
||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d
|
||||
github.com/mudler/yip v0.11.5-0.20230124143654-91e88dfb6648
|
||||
github.com/nxadm/tail v1.4.8
|
||||
github.com/onsi/ginkgo/v2 v2.9.2
|
||||
github.com/onsi/gomega v1.27.6
|
||||
github.com/pterm/pterm v0.12.59
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46
|
||||
github.com/swaggest/jsonschema-go v0.3.49
|
||||
github.com/urfave/cli/v2 v2.25.1
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/oauth2 v0.7.0
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
atomicgo.dev/cursor v0.1.1 // indirect
|
||||
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||
github.com/StackExchange/wmi v1.2.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1 // indirect
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1 // indirect
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
|
||||
github.com/charmbracelet/bubbles v0.14.0 // indirect
|
||||
github.com/charmbracelet/bubbletea v0.23.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.6.0 // indirect
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 // indirect
|
||||
github.com/codingsince1985/checksum v1.2.6 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/denisbrodbeck/machineid v1.0.1 // indirect
|
||||
github.com/disintegration/imaging v1.6.2 // indirect
|
||||
github.com/eliukblau/pixterm v1.3.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gookit/color v1.5.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||
github.com/jaypipes/pcidb v1.0.0 // indirect
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/kairos-io/kairos v1.24.3-56.0.20230329142538-b6ae4b58c07d // indirect
|
||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.5 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/makiuchi-d/gozxing v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.13.0 // indirect
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/qeesung/image2ascii v1.0.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/swaggest/refl v1.1.0 // indirect
|
||||
github.com/twpayne/go-vfs v1.7.2 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
github.com/zcalusic/sysinfo v0.9.5 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
howett.net/plist v1.0.0 // indirect
|
||||
)
|
462
go.sum
462
go.sum
@ -1,462 +0,0 @@
|
||||
atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
|
||||
atomicgo.dev/cursor v0.1.1 h1:0t9sxQomCTRh5ug+hAMCs59x/UmC9QL6Ci5uosINKD4=
|
||||
atomicgo.dev/cursor v0.1.1/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU=
|
||||
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
|
||||
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
|
||||
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
|
||||
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
|
||||
github.com/MarvinJWendt/testza v0.2.10/go.mod h1:pd+VWsoGUiFtq+hRKSU1Bktnn+DMCSrDrXDpX2bG66k=
|
||||
github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzXjB69adAhzZkI=
|
||||
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
|
||||
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
||||
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 h1:WWB576BN5zNSZc/M9d/10pqEx5VHNhaQ/yOVAkmj5Yo=
|
||||
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E=
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/bool64/dev v0.2.27 h1:mFT+B74mFVgUeUmm/EbfM6ELPA55lEXBjQ/AOHCwCOc=
|
||||
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1 h1:BKTqrqXiQYovrDlfuVFaEGz0r4Ou6EED8L7jCXw6Buw=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yPXQTPshACY8vQ0=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
|
||||
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
||||
github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc=
|
||||
github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4=
|
||||
github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUIte8WPvhck=
|
||||
github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
|
||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
||||
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9 h1:xz6Nv3zcwO2Lila35hcb0QloCQsc38Al13RNEzWRpX4=
|
||||
github.com/chuckpreslar/emission v0.0.0-20170206194824-a7ddd980baf9/go.mod h1:2wSM9zJkl1UQEFZgSd68NfCgRz1VL1jzy/RjCg+ULrs=
|
||||
github.com/codingsince1985/checksum v1.2.6 h1:UjCDls6oaRQeLPG14TvjLvOos2XL1qHdMl8uGMkzpi8=
|
||||
github.com/codingsince1985/checksum v1.2.6/go.mod h1:Pe5wfeiqzQC1qEXLWEFmxQ3W/OklJEJGiJO62graCJU=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
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/eliukblau/pixterm v1.3.1 h1:XeouQViH+lmzCa7sMUoK2cd7qlgHYGLIjwRKaOdJbKA=
|
||||
github.com/eliukblau/pixterm v1.3.1/go.mod h1:on5ueknFt+ZFVvIVVzQ7/JXwPjv5fJd8Q1Ybh7XixfU=
|
||||
github.com/erikgeiser/promptkit v0.8.0 h1:bvOzPs6RLyfRZDSgVWOghQEiBSRHQ3zmDdxcV8zOc+E=
|
||||
github.com/erikgeiser/promptkit v0.8.0/go.mod h1:QxyFbCrrj20PyvV5b+ckWPozbgX11s04GeRlmTCIMTo=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v40 v40.0.0 h1:oBPVDaIhdUmwDWRRH8XJ/dZG+Rn755i08+Hp1uJHlR0=
|
||||
github.com/google/go-github/v40 v40.0.0/go.mod h1:G8wWKTEjUCL0zdbaQvpwDk0hqf6KZgPQH+ssJa+/NVc=
|
||||
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-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
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.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE=
|
||||
github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/orderedmap v0.2.0 h1:sq1N/TFpYH++aViPcaKjys3bDClUEU7s5B+z6jq8pNA=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
|
||||
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
|
||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||
github.com/itchyny/gojq v0.12.12 h1:x+xGI9BXqKoJQZkr95ibpe3cdrTbY8D9lonrK433rcA=
|
||||
github.com/itchyny/gojq v0.12.12/go.mod h1:j+3sVkjxwd7A7Z5jrbKibgOLn0ZfLWkV+Awxr/pyzJE=
|
||||
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
|
||||
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
||||
github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY=
|
||||
github.com/jaypipes/ghw v0.10.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g=
|
||||
github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8=
|
||||
github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240 h1:dy+DS31tGEGCsZzB45HmJJNHjur8GDgtRNX9U7HnSX4=
|
||||
github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kairos-io/kairos v1.24.3-56.0.20230329142538-b6ae4b58c07d h1:B01GinEZbowPwbWrqDIb6n2AaHIscJVOqsh0I5gAEXw=
|
||||
github.com/kairos-io/kairos v1.24.3-56.0.20230329142538-b6ae4b58c07d/go.mod h1:2aYSSCHw8csfuqA5g6BpxBJ89kZt84G5okeuJj7PH+w=
|
||||
github.com/kairos-io/kairos-sdk v0.0.2-0.20230414094028-0c9d2bd9e6ae h1:u/1QiU5IAJNDPxsBWBQFKQxLJKcDog1aMrN0unaP18w=
|
||||
github.com/kairos-io/kairos-sdk v0.0.2-0.20230414094028-0c9d2bd9e6ae/go.mod h1:Wg/jfAQe8seka5VUXtcPvg+sA6GmQEy+DYlJmgKM8Zs=
|
||||
github.com/kairos-io/kcrypt v0.5.2 h1:F9jbIjk3+nSQYEoSTDXT118Cx8AjmtDrMcU4rq/WBsI=
|
||||
github.com/kairos-io/kcrypt v0.5.2/go.mod h1:FdhkFI5/gbHWr15Pvd40IAttWvxnF8/XUghMsezj1fs=
|
||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329 h1:qq2nCpSrXrmvDGRxW0ruW9BVEV1CN2a9YDOExdt+U0o=
|
||||
github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c=
|
||||
github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
|
||||
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
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-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb h1:F6TP0DW7C0U9sgm9g4uAs0Vp2JSkhn2umlyrNlxUKXw=
|
||||
github.com/mudler/go-nodepair v0.0.0-20221223092639-ba399a66fdfb/go.mod h1:6QsBHK5vY/wDnSpueKek3/qfoCZmdJ9pz7FnzQbP/gE=
|
||||
github.com/mudler/go-pluggable v0.0.0-20230126220627-7710299a0ae5 h1:FaZD86+A9mVt7lh9glAryzQblMsbJYU2VnrdZ8yHlTs=
|
||||
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-20220724164624-c45b5c61312d h1:/lAg9vPAAU+s35cDMCx1IyeMn+4OYfCBPqi08Q8vXDg=
|
||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d/go.mod h1:HGGAOJhipApckwNV8ZTliRJqxctUv3xRY+zbQEwuytc=
|
||||
github.com/mudler/yip v0.11.5-0.20230124143654-91e88dfb6648 h1:+UZPjgWOTB1LyWI5qHTReIGXDSlXynGl2kIihi/lU98=
|
||||
github.com/mudler/yip v0.11.5-0.20230124143654-91e88dfb6648/go.mod h1:7d0bnZ326k/bmeTvLZL5ZQx4QNi0a7mfrnGzb+2ZkrA=
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4=
|
||||
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
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.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
|
||||
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
|
||||
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
|
||||
github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEejaWgXU=
|
||||
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
|
||||
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
|
||||
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
|
||||
github.com/pterm/pterm v0.12.59 h1:VBStvXiZL+6L+nNYjlXsD/035RJF5crqOvgqAm/Rvns=
|
||||
github.com/pterm/pterm v0.12.59/go.mod h1:Lt90KhnId704siiQtMZiLS7UfoC7TRUM1HufzdM0kjk=
|
||||
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/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.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 h1:uIkTLo0AGRc8l7h5l9r+GcYi9qfVPt6lD4/bhmzfiKo=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46 h1:q2T2RnISqPdZWvUpBQw0n7QWtF4cNo5RpCDTZmV732M=
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46/go.mod h1:L2fIdtZqbQEagjOOXwkwH3t7MjJUd7fbt52cLSQGDBg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/swaggest/assertjson v1.7.0 h1:SKw5Rn0LQs6UvmGrIdaKQbMR1R3ncXm5KNon+QJ7jtw=
|
||||
github.com/swaggest/jsonschema-go v0.3.49 h1:0dB6+6/uuU9lH41evLVum9Ui1b1Pkm1mjtsHWYL+y30=
|
||||
github.com/swaggest/jsonschema-go v0.3.49/go.mod h1:fZmC8juuqFTMe4Fc9tHJXwG+Uaf9BKYvF8ygL+asOuY=
|
||||
github.com/swaggest/refl v1.1.0 h1:a+9a75Kv6ciMozPjVbOfcVTEQe81t2R3emvaD9oGQGc=
|
||||
github.com/swaggest/refl v1.1.0/go.mod h1:g3Qa6ki0A/L2yxiuUpT+cuBURuRaltF5SDQpg1kMZSY=
|
||||
github.com/twpayne/go-vfs v1.7.2 h1:ZNYMAXcu2Av8c109USrSGYm8dIIIV0xPlG19I2088Kw=
|
||||
github.com/twpayne/go-vfs v1.7.2/go.mod h1:1eni2ntkiiAHZG27xfLOO4CYvMR4Kw8V7rYiLeeolsQ=
|
||||
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
|
||||
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0 h1:EB7cIzBdsOzAgmhTUtTTQXBByuPheP/Zv1zL2BRPY6g=
|
||||
github.com/wayneashleyberry/terminal-dimensions v1.1.0/go.mod h1:2lc/0eWCObmhRczn2SdGSQtgBooLUzIotkkEGXqghyg=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zcalusic/sysinfo v0.9.5 h1:ivoHyj9aIAYkwzo1+8QgJ5s4oeE6Etx9FmZtqa4wJjQ=
|
||||
github.com/zcalusic/sysinfo v0.9.5/go.mod h1:Z/gPVufBrFc8X5sef3m6kkw3r3nlNFp+I6bvASfvBZQ=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
|
||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43 h1:gQ6GUSD102fPgli+Yb4cR/cGaHF7tNBt+GYoRCpGC7s=
|
||||
golang.org/x/image v0.0.0-20191206065243-da761ea9ff43/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-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.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
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=
|
||||
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.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU=
|
||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
@ -1,95 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/nxadm/tail"
|
||||
)
|
||||
|
||||
// Run starts the agent provider emitting the bootstrap event.
|
||||
func Run(opts ...Option) error {
|
||||
o := &Options{}
|
||||
if err := o.Apply(opts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
os.MkdirAll("/usr/local/.kairos", 0600) //nolint:errcheck
|
||||
|
||||
// Reads config
|
||||
c, err := config.Scan(collector.Directories(o.Dir...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
bf := machine.BootFrom()
|
||||
if c.Install != nil && c.Install.Auto && (bf == machine.NetBoot || bf == machine.LiveCDBoot) {
|
||||
// Don't go ahead if we are asked to install from a booting live medium
|
||||
fmt.Println("Agent run aborted. Installation being performed from live medium")
|
||||
return nil
|
||||
}
|
||||
|
||||
os.MkdirAll("/var/log/kairos", 0600) //nolint:errcheck
|
||||
|
||||
fileName := filepath.Join("/var/log/kairos", "agent-provider.log")
|
||||
|
||||
// Create if not exist
|
||||
if _, err := os.Stat(fileName); err != nil {
|
||||
err = os.WriteFile(fileName, []byte{}, os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Tail to the log
|
||||
t, err := tail.TailFile(fileName, tail.Config{Follow: true})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
for line := range t.Lines {
|
||||
fmt.Println(line.Text)
|
||||
}
|
||||
}()
|
||||
|
||||
if !machine.SentinelExist("firstboot") {
|
||||
|
||||
if err := hook.Run(*c, hook.FirstBoot...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Re-load providers
|
||||
bus.Reload()
|
||||
err = machine.CreateSentinel("firstboot")
|
||||
if c.FailOnBundleErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Re-read config files
|
||||
c, err = config.Scan(collector.Directories(o.Dir...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
configStr, err := c.Config.String()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = bus.Manager.Publish(events.EventBootstrap, events.BootstrapPayload{APIAddress: o.APIAddress, Config: configStr, Logfile: fileName})
|
||||
|
||||
if o.Restart && err != nil {
|
||||
fmt.Println("Warning: Agent failed, restarting: ", err.Error())
|
||||
return Run(opts...)
|
||||
}
|
||||
return err
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package agent
|
||||
|
||||
// Options yields the options for the running agent.
|
||||
type Options struct {
|
||||
APIAddress string
|
||||
Dir []string
|
||||
Force bool
|
||||
Restart bool
|
||||
}
|
||||
|
||||
// Apply applies option to the options struct.
|
||||
func (o *Options) Apply(opts ...Option) error {
|
||||
for _, oo := range opts {
|
||||
if err := oo(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Option is a generic option for the Agent.
|
||||
type Option func(o *Options) error
|
||||
|
||||
// ForceAgent forces the agent to run.
|
||||
var ForceAgent Option = func(o *Options) error {
|
||||
o.Force = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartAgent makes the agent restart on error.
|
||||
var RestartAgent Option = func(o *Options) error {
|
||||
o.Restart = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithAPI sets the API address used to talk to EdgeVPN and co-ordinate node bootstrapping.
|
||||
func WithAPI(address string) Option {
|
||||
return func(o *Options) error {
|
||||
o.APIAddress = address
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithDirectory sets the Agent config directories.
|
||||
func WithDirectory(dirs ...string) Option {
|
||||
return func(o *Options) error {
|
||||
o.Dir = dirs
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package agent_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Agent Suite")
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package agent_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/internal/agent"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const testProvider = `#!/bin/bash
|
||||
event="$1"
|
||||
payload=$(</dev/stdin)
|
||||
echo "Received $event with $payload" >> exec.log
|
||||
echo "{}"
|
||||
`
|
||||
|
||||
var _ = Describe("Bootstrap provider", func() {
|
||||
Context("Config", func() {
|
||||
It("gets entire content", func() {
|
||||
f, err := ioutil.TempDir("", "tests")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(f)
|
||||
|
||||
wd, _ := os.Getwd()
|
||||
os.WriteFile(filepath.Join(wd, "agent-provider-test"), []byte(testProvider), 0655)
|
||||
|
||||
defer os.RemoveAll(filepath.Join(wd, "agent-provider-test"))
|
||||
|
||||
err = os.WriteFile(filepath.Join(f, "test.config.yaml"), []byte(`#cloud-config
|
||||
doo: bar`), 0655)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
bus.Manager.Initialize()
|
||||
err = Run(WithDirectory(f))
|
||||
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
dat, err := os.ReadFile(filepath.Join(wd, "exec.log"))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
fmt.Println(string(dat))
|
||||
Expect(string(dat)).To(ContainSubstring("Received"), string(dat))
|
||||
Expect(string(dat)).To(ContainSubstring("doo: bar"), string(dat))
|
||||
})
|
||||
})
|
||||
})
|
@ -1,75 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/kairos"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type BrandingText struct {
|
||||
InteractiveInstall string `yaml:"interactive-install"`
|
||||
Install string `yaml:"install"`
|
||||
Reset string `yaml:"reset"`
|
||||
Recovery string `yaml:"recovery"`
|
||||
}
|
||||
type WebUI struct {
|
||||
Disable bool `yaml:"disable"`
|
||||
ListenAddress string `yaml:"listen_address"`
|
||||
}
|
||||
|
||||
func (w WebUI) HasAddress() bool {
|
||||
return w.ListenAddress != ""
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Fast bool `yaml:"fast,omitempty"`
|
||||
WebUI WebUI `yaml:"webui"`
|
||||
Branding BrandingText `yaml:"branding"`
|
||||
}
|
||||
|
||||
func LoadConfig(path ...string) (*Config, error) {
|
||||
if len(path) == 0 {
|
||||
path = append(path, "/etc/kairos/agent.yaml", "/etc/elemental/config.yaml")
|
||||
}
|
||||
|
||||
cfg := &Config{}
|
||||
|
||||
for _, p := range path {
|
||||
f, err := os.ReadFile(p)
|
||||
if err == nil {
|
||||
yaml.Unmarshal(f, cfg) //nolint:errcheck
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Branding.InteractiveInstall == "" {
|
||||
f, err := os.ReadFile(kairos.BrandingFile("interactive_install_text"))
|
||||
if err == nil {
|
||||
cfg.Branding.InteractiveInstall = string(f)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Branding.Install == "" {
|
||||
f, err := os.ReadFile(kairos.BrandingFile("install_text"))
|
||||
if err == nil {
|
||||
cfg.Branding.Install = string(f)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Branding.Recovery == "" {
|
||||
f, err := os.ReadFile(kairos.BrandingFile("recovery_text"))
|
||||
if err == nil {
|
||||
cfg.Branding.Recovery = string(f)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Branding.Reset == "" {
|
||||
f, err := os.ReadFile(kairos.BrandingFile("reset_text"))
|
||||
if err == nil {
|
||||
cfg.Branding.Reset = string(f)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
)
|
||||
|
||||
type BundleOption struct{}
|
||||
|
||||
func (b BundleOption) Run(c config.Config) error {
|
||||
|
||||
machine.Mount("COS_PERSISTENT", "/usr/local") //nolint:errcheck
|
||||
defer func() {
|
||||
machine.Umount("/usr/local") //nolint:errcheck
|
||||
}()
|
||||
|
||||
machine.Mount("COS_OEM", "/oem") //nolint:errcheck
|
||||
defer func() {
|
||||
machine.Umount("/oem") //nolint:errcheck
|
||||
}()
|
||||
|
||||
opts := c.Install.Bundles.Options()
|
||||
err := bundles.RunBundles(opts...)
|
||||
if c.FailOnBundleErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type BundlePostInstall struct{}
|
||||
|
||||
func (b BundlePostInstall) Run(c config.Config) error {
|
||||
opts := c.Bundles.Options()
|
||||
err := bundles.RunBundles(opts...)
|
||||
if c.FailOnBundleErrors && err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/system"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
)
|
||||
|
||||
type GrubOptions struct{}
|
||||
|
||||
func (b GrubOptions) Run(c config.Config) error {
|
||||
err := system.Apply(system.SetGRUBOptions(c.Install.GrubOptions))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GrubPostInstallOptions struct{}
|
||||
|
||||
func (b GrubPostInstallOptions) Run(c config.Config) error {
|
||||
err := system.Apply(system.SetGRUBOptions(c.GrubOptions))
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
Run(c config.Config) error
|
||||
}
|
||||
|
||||
var AfterInstall = []Interface{
|
||||
&RunStage{}, // Shells out to stages defined from the container image
|
||||
&GrubOptions{}, // Set custom GRUB options
|
||||
&BundleOption{},
|
||||
&CustomMounts{},
|
||||
&Kcrypt{},
|
||||
&Lifecycle{}, // Handles poweroff/reboot by config options
|
||||
}
|
||||
|
||||
var AfterReset = []Interface{}
|
||||
|
||||
var FirstBoot = []Interface{
|
||||
&BundlePostInstall{},
|
||||
&GrubPostInstallOptions{},
|
||||
}
|
||||
|
||||
func Run(c config.Config, hooks ...Interface) error {
|
||||
for _, h := range hooks {
|
||||
if err := h.Run(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
|
||||
kcryptconfig "github.com/kairos-io/kcrypt/pkg/config"
|
||||
)
|
||||
|
||||
type Kcrypt struct{}
|
||||
|
||||
func (k Kcrypt) Run(c config.Config) error {
|
||||
|
||||
if len(c.Install.Encrypt) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
machine.Mount("COS_OEM", "/oem") //nolint:errcheck
|
||||
defer func() {
|
||||
machine.Umount("/oem") //nolint:errcheck
|
||||
}()
|
||||
|
||||
kcryptc, err := kcryptconfig.GetConfiguration(kcryptconfig.ConfigScanDirs)
|
||||
if err != nil {
|
||||
fmt.Println("Failed getting kcrypt configuration: ", err.Error())
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range c.Install.Encrypt {
|
||||
out, err := utils.SH(fmt.Sprintf("kcrypt encrypt %s", p))
|
||||
if err != nil {
|
||||
fmt.Printf("could not encrypt partition: %s\n", out+err.Error())
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
// Give time to show the error
|
||||
time.Sleep(10 * time.Second)
|
||||
return nil // do not error out
|
||||
}
|
||||
|
||||
err = kcryptc.SetMapping(strings.TrimSpace(out))
|
||||
if err != nil {
|
||||
fmt.Println("Failed updating the kcrypt configuration file: ", err.Error())
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = kcryptc.WriteMappings(kcryptconfig.MappingsFile)
|
||||
if err != nil {
|
||||
fmt.Println("Failed writing kcrypt partition mappings: ", err.Error())
|
||||
if c.FailOnBundleErrors {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
)
|
||||
|
||||
type Lifecycle struct{}
|
||||
|
||||
func (s Lifecycle) Run(c config.Config) error {
|
||||
if c.Install.Reboot {
|
||||
utils.Reboot()
|
||||
}
|
||||
|
||||
if c.Install.Poweroff {
|
||||
utils.PowerOFF()
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/mudler/yip/pkg/schema"
|
||||
yip "github.com/mudler/yip/pkg/schema"
|
||||
"gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
type CustomMounts struct{}
|
||||
|
||||
func saveCloudConfig(name config.Stage, yc yip.YipConfig) error {
|
||||
yipYAML, err := yaml.Marshal(yc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join("/oem", fmt.Sprintf("10_%s.yaml", name)), yipYAML, 0400)
|
||||
}
|
||||
|
||||
// Read the keys sections ephemeral_mounts and bind mounts from install key in the cloud config.
|
||||
// If not empty write an environment file to /run/cos/custom-layout.env.
|
||||
// That env file is in turn read by /overlay/files/system/oem/11_persistency.yaml in fs.after stage.
|
||||
func (cm CustomMounts) Run(c config.Config) error {
|
||||
|
||||
//fmt.Println("Custom mounts hook")
|
||||
//fmt.Println(strings.Join(c.Install.BindMounts, " "))
|
||||
//fmt.Println(strings.Join(c.Install.EphemeralMounts, " "))
|
||||
|
||||
if len(c.Install.BindMounts) == 0 && len(c.Install.EphemeralMounts) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
machine.Mount("COS_OEM", "/oem") //nolint:errcheck
|
||||
defer func() {
|
||||
machine.Umount("/oem") //nolint:errcheck
|
||||
}()
|
||||
|
||||
var mountsList = map[string]string{}
|
||||
|
||||
mountsList["CUSTOM_BIND_MOUNTS"] = strings.Join(c.Install.BindMounts, " ")
|
||||
mountsList["CUSTOM_EPHEMERAL_MOUNTS"] = strings.Join(c.Install.EphemeralMounts, " ")
|
||||
|
||||
config := yip.YipConfig{Stages: map[string][]schema.Stage{
|
||||
"rootfs": []yip.Stage{{
|
||||
Name: "user_custom_mounts",
|
||||
EnvironmentFile: "/run/cos/custom-layout.env",
|
||||
Environment: mountsList,
|
||||
}},
|
||||
}}
|
||||
|
||||
saveCloudConfig("user_custom_mounts", config) //nolint:errcheck
|
||||
return nil
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
)
|
||||
|
||||
type RunStage struct{}
|
||||
|
||||
func (r RunStage) Run(_ config.Config) error {
|
||||
utils.SH("elemental run-stage kairos-install.after") //nolint:errcheck
|
||||
events.RunHookScript("/usr/bin/kairos-agent.install.after.hook") //nolint:errcheck
|
||||
return nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,365 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
qr "github.com/mudler/go-nodepair/qrcode"
|
||||
"github.com/mudler/go-pluggable"
|
||||
"github.com/pterm/pterm"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func optsToArgs(options map[string]string) (res []string) {
|
||||
for k, v := range options {
|
||||
if k != "device" && k != "cc" && k != "reboot" && k != "poweroff" {
|
||||
res = append(res, fmt.Sprintf("--%s", k))
|
||||
if v != "" {
|
||||
res = append(res, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func displayInfo(agentConfig *Config) {
|
||||
fmt.Println("--------------------------")
|
||||
fmt.Println("No providers found, dropping to a shell. \n -- For instructions on how to install manually, see: https://kairos.io/docs/installation/manual/")
|
||||
if !agentConfig.WebUI.Disable {
|
||||
if !agentConfig.WebUI.HasAddress() {
|
||||
ips := machine.LocalIPs()
|
||||
if len(ips) > 0 {
|
||||
fmt.Print("WebUI installer running at : ")
|
||||
for _, ip := range ips {
|
||||
fmt.Printf("%s%s ", ip, config.DefaultWebUIListenAddress)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("WebUI installer running at : %s\n", agentConfig.WebUI.ListenAddress)
|
||||
}
|
||||
|
||||
ifaces := machine.Interfaces()
|
||||
fmt.Printf("Network Interfaces: %s\n", strings.Join(ifaces, " "))
|
||||
}
|
||||
}
|
||||
|
||||
func mergeOption(cloudConfig string, r map[string]string) {
|
||||
c := &config.Config{}
|
||||
yaml.Unmarshal([]byte(cloudConfig), c) //nolint:errcheck
|
||||
for k, v := range c.Options {
|
||||
if k == "cc" {
|
||||
continue
|
||||
}
|
||||
r[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
func ManualInstall(c string, options map[string]string, strictValidations bool) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
source, err := prepareConfiguration(ctx, c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cc, err := config.Scan(collector.Directories(source), collector.MergeBootLine, collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
configStr, err := cc.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options["cc"] = configStr
|
||||
// unlike Install device is already set
|
||||
// options["device"] = cc.Install.Device
|
||||
|
||||
mergeOption(configStr, options)
|
||||
|
||||
if options["device"] == "" {
|
||||
options["device"] = cc.Install.Device
|
||||
}
|
||||
|
||||
return RunInstall(options)
|
||||
}
|
||||
|
||||
func Install(dir ...string) error {
|
||||
utils.OnSignal(func() {
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint:errcheck
|
||||
}
|
||||
}, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
tk := ""
|
||||
r := map[string]string{}
|
||||
|
||||
bus.Manager.Response(events.EventChallenge, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
tk = r.Data
|
||||
})
|
||||
|
||||
bus.Manager.Response(events.EventInstall, func(p *pluggable.Plugin, resp *pluggable.EventResponse) {
|
||||
err := json.Unmarshal([]byte(resp.Data), &r)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
|
||||
ensureDataSourceReady()
|
||||
|
||||
// Reads config, and if present and offline is defined,
|
||||
// runs the installation
|
||||
cc, err := config.Scan(collector.Directories(dir...), collector.MergeBootLine, collector.NoLogs)
|
||||
if err == nil && cc.Install != nil && cc.Install.Auto {
|
||||
configStr, err := cc.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r["cc"] = configStr
|
||||
r["device"] = cc.Install.Device
|
||||
mergeOption(configStr, r)
|
||||
|
||||
err = RunInstall(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint:errcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("- config not found in the system: %s", err.Error())
|
||||
}
|
||||
|
||||
agentConfig, err := LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try to clear screen
|
||||
cmd.ClearScreen()
|
||||
cmd.PrintBranding(DefaultBanner)
|
||||
|
||||
// If there are no providers registered, we enter a shell for manual installation
|
||||
// and print information about the webUI
|
||||
if !bus.Manager.HasRegisteredPlugins() {
|
||||
displayInfo(agentConfig)
|
||||
return utils.Shell().Run()
|
||||
}
|
||||
|
||||
configStr, err := cc.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bus.Manager.Publish(events.EventChallenge, events.EventPayload{Config: configStr})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PrintText(agentConfig.Branding.Install, "Installation")
|
||||
|
||||
if !agentConfig.Fast {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
if tk != "" {
|
||||
qr.Print(tk)
|
||||
}
|
||||
|
||||
if _, err := bus.Manager.Publish(events.EventInstall, events.InstallPayload{Token: tk, Config: configStr}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return errors.New("no configuration, stopping installation")
|
||||
}
|
||||
|
||||
// we receive a cloud config at this point
|
||||
cloudConfig, exists := r["cc"]
|
||||
|
||||
// merge any options defined in it
|
||||
mergeOption(cloudConfig, r)
|
||||
|
||||
// now merge cloud config from system and
|
||||
// the one received from the agent-provider
|
||||
ccData := map[string]interface{}{}
|
||||
|
||||
// make sure the config we write has at least the #cloud-config header,
|
||||
// if any other was defined beforeahead
|
||||
header := "#cloud-config"
|
||||
if hasHeader, head := config.HasHeader(configStr, ""); hasHeader {
|
||||
header = head
|
||||
}
|
||||
|
||||
// What we receive take precedence over the one in the system. best-effort
|
||||
yaml.Unmarshal([]byte(configStr), &ccData) //nolint:errcheck
|
||||
if exists {
|
||||
yaml.Unmarshal([]byte(cloudConfig), &ccData) //nolint:errcheck
|
||||
if hasHeader, head := config.HasHeader(cloudConfig, ""); hasHeader {
|
||||
header = head
|
||||
}
|
||||
}
|
||||
|
||||
out, err := yaml.Marshal(ccData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed marshalling cc: %w", err)
|
||||
}
|
||||
|
||||
r["cc"] = config.AddHeader(header, string(out))
|
||||
|
||||
pterm.Info.Println("Starting installation")
|
||||
|
||||
if err := RunInstall(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.Info.Println("Installation completed, press enter to go back to the shell.")
|
||||
|
||||
utils.Prompt("") //nolint:errcheck
|
||||
|
||||
// give tty1 back
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint: errcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunInstall(options map[string]string) error {
|
||||
utils.SH("elemental run-stage kairos-install.pre") //nolint:errcheck
|
||||
events.RunHookScript("/usr/bin/kairos-agent.install.pre.hook") //nolint:errcheck
|
||||
|
||||
f, _ := os.CreateTemp("", "xxxx")
|
||||
defer os.RemoveAll(f.Name())
|
||||
|
||||
device, ok := options["device"]
|
||||
if !ok {
|
||||
fmt.Println("device must be specified among options")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if device == "auto" {
|
||||
device = detectDevice()
|
||||
}
|
||||
|
||||
cloudInit, ok := options["cc"]
|
||||
if !ok {
|
||||
fmt.Println("cloudInit must be specified among options")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
c := &config.Config{}
|
||||
yaml.Unmarshal([]byte(cloudInit), c) //nolint:errcheck
|
||||
|
||||
_, reboot := options["reboot"]
|
||||
_, poweroff := options["poweroff"]
|
||||
if c.Install == nil {
|
||||
c.Install = &config.Install{}
|
||||
}
|
||||
if poweroff {
|
||||
c.Install.Poweroff = true
|
||||
}
|
||||
if reboot {
|
||||
c.Install.Reboot = true
|
||||
}
|
||||
|
||||
if c.Install.Image != "" {
|
||||
options["system.uri"] = c.Install.Image
|
||||
}
|
||||
|
||||
env := append(c.Install.Env, c.Env...)
|
||||
utils.SetEnv(env)
|
||||
|
||||
err := os.WriteFile(f.Name(), []byte(cloudInit), os.ModePerm)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write cloud init: %s\n", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
args := []string{"install"}
|
||||
args = append(args, optsToArgs(options)...)
|
||||
args = append(args, "-c", f.Name(), device)
|
||||
|
||||
cmd := exec.Command("elemental", args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return hook.Run(*c, hook.AfterInstall...)
|
||||
}
|
||||
|
||||
func ensureDataSourceReady() {
|
||||
timeout := time.NewTimer(5 * time.Minute)
|
||||
ticker := time.NewTicker(500 * time.Millisecond)
|
||||
|
||||
defer timeout.Stop()
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
fmt.Println("userdata configuration failed to load after 5m, ignoring.")
|
||||
return
|
||||
case <-ticker.C:
|
||||
if _, err := os.Stat("/run/.userdata_load"); os.IsNotExist(err) {
|
||||
return
|
||||
}
|
||||
fmt.Println("userdata configuration has not yet completed. (waiting for /run/.userdata_load to be deleted)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prepareConfiguration(ctx context.Context, source string) (string, error) {
|
||||
// if the source is not an url it is already a configuration path
|
||||
if u, err := url.Parse(source); err != nil || u.Scheme == "" {
|
||||
return source, nil
|
||||
}
|
||||
|
||||
// create a configuration file with the source referenced
|
||||
f, err := os.CreateTemp(os.TempDir(), "kairos-install-*.yaml")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// defer cleanup until after parent is done
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
_ = os.RemoveAll(f.Name())
|
||||
}()
|
||||
|
||||
cfg := config.Config{
|
||||
ConfigURL: source,
|
||||
}
|
||||
if err = yaml.NewEncoder(f).Encode(cfg); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("prepareConfiguration", func() {
|
||||
path := "/foo/bar"
|
||||
url := "https://example.com"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
It("returns a file path with no modifications", func() {
|
||||
source, err := prepareConfiguration(ctx, path)
|
||||
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(source).To(Equal(path))
|
||||
})
|
||||
|
||||
It("creates a configuration file containing the given url", func() {
|
||||
source, err := prepareConfiguration(ctx, url)
|
||||
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(source).ToNot(Equal(path))
|
||||
|
||||
f, err := os.Open(source)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var cfg config.Config
|
||||
err = yaml.NewDecoder(f).Decode(&cfg)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(cfg.ConfigURL).To(Equal(url))
|
||||
})
|
||||
|
||||
It("cleans up the configuration file after context is done", func() {
|
||||
source, err := prepareConfiguration(ctx, url)
|
||||
cancel()
|
||||
|
||||
_, err = os.Stat(source)
|
||||
Expect(os.IsNotExist(err))
|
||||
})
|
||||
})
|
@ -1,276 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
config "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/unstructured"
|
||||
|
||||
"github.com/erikgeiser/promptkit/textinput"
|
||||
"github.com/jaypipes/ghw"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/mudler/go-pluggable"
|
||||
"github.com/mudler/yip/pkg/schema"
|
||||
"github.com/pterm/pterm"
|
||||
)
|
||||
|
||||
const (
|
||||
canBeEmpty = "Unset"
|
||||
yesNo = "[y]es/[N]o"
|
||||
)
|
||||
|
||||
func prompt(prompt, initialValue, placeHolder string, canBeEmpty, hidden bool) (string, error) {
|
||||
input := textinput.New(prompt)
|
||||
input.InitialValue = initialValue
|
||||
input.Placeholder = placeHolder
|
||||
if canBeEmpty {
|
||||
input.Validate = func(s string) error { return nil }
|
||||
}
|
||||
input.Hidden = hidden
|
||||
|
||||
return input.RunPrompt()
|
||||
}
|
||||
|
||||
func isYes(s string) bool {
|
||||
i := strings.ToLower(s)
|
||||
if i == "y" || i == "yes" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
_ = 1 << (10 * iota)
|
||||
KiB
|
||||
MiB
|
||||
GiB
|
||||
TiB
|
||||
)
|
||||
|
||||
func promptBool(p events.YAMLPrompt) (string, error) {
|
||||
def := "n"
|
||||
if p.Default != "" {
|
||||
def = p.Default
|
||||
}
|
||||
val, err := prompt(p.Prompt, def, yesNo, true, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isYes(val) {
|
||||
val = "true"
|
||||
} else {
|
||||
val = "false"
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func promptText(p events.YAMLPrompt) (string, error) {
|
||||
def := ""
|
||||
if p.Default != "" {
|
||||
def = p.Default
|
||||
}
|
||||
return prompt(p.Prompt, def, p.PlaceHolder, true, false)
|
||||
}
|
||||
|
||||
func promptToUnstructured(p events.YAMLPrompt, unstructuredYAML map[string]interface{}) (map[string]interface{}, error) {
|
||||
var res string
|
||||
if p.AskFirst {
|
||||
ask, err := prompt(p.AskPrompt, "n", yesNo, true, false)
|
||||
if err == nil && !isYes(ask) {
|
||||
return unstructuredYAML, nil
|
||||
}
|
||||
}
|
||||
if p.Bool {
|
||||
val, err := promptBool(p)
|
||||
if err != nil {
|
||||
return unstructuredYAML, err
|
||||
}
|
||||
unstructuredYAML[p.YAMLSection] = val
|
||||
res = val
|
||||
} else {
|
||||
val, err := promptText(p)
|
||||
if err != nil {
|
||||
return unstructuredYAML, err
|
||||
}
|
||||
unstructuredYAML[p.YAMLSection] = val
|
||||
res = val
|
||||
}
|
||||
|
||||
if res == "" && p.IfEmpty != "" {
|
||||
res = p.IfEmpty
|
||||
unstructuredYAML[p.YAMLSection] = res
|
||||
}
|
||||
return unstructuredYAML, nil
|
||||
}
|
||||
|
||||
func detectDevice() string {
|
||||
preferedDevice := "/dev/sda"
|
||||
maxSize := float64(0)
|
||||
|
||||
block, err := ghw.Block()
|
||||
if err == nil {
|
||||
for _, disk := range block.Disks {
|
||||
size := float64(disk.SizeBytes) / float64(GiB)
|
||||
if size > maxSize {
|
||||
maxSize = size
|
||||
preferedDevice = "/dev/" + disk.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
return preferedDevice
|
||||
}
|
||||
|
||||
func InteractiveInstall(spawnShell bool) error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
cmd.PrintBranding(DefaultBanner)
|
||||
|
||||
agentConfig, err := LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PrintText(agentConfig.Branding.InteractiveInstall, "Installation")
|
||||
|
||||
disks := []string{}
|
||||
maxSize := float64(0)
|
||||
preferedDevice := "/dev/sda"
|
||||
|
||||
block, err := ghw.Block()
|
||||
if err == nil {
|
||||
for _, disk := range block.Disks {
|
||||
size := float64(disk.SizeBytes) / float64(GiB)
|
||||
if size > maxSize {
|
||||
maxSize = size
|
||||
preferedDevice = "/dev/" + disk.Name
|
||||
}
|
||||
disks = append(disks, fmt.Sprintf("/dev/%s: %s (%.2f GiB) ", disk.Name, disk.Model, float64(disk.SizeBytes)/float64(GiB)))
|
||||
}
|
||||
}
|
||||
|
||||
pterm.Info.Println("Available Disks:")
|
||||
for _, d := range disks {
|
||||
pterm.Info.Println(" " + d)
|
||||
}
|
||||
|
||||
device, err := prompt("What's the target install device?", preferedDevice, "Cannot be empty", false, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userName, err := prompt("User to setup", "kairos", canBeEmpty, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
userPassword, err := prompt("Password", "", canBeEmpty, true, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userPassword == "" {
|
||||
userPassword = "!"
|
||||
}
|
||||
|
||||
users, err := prompt("SSH access (rsakey, github/gitlab supported, comma-separated)", "github:someuser,github:someuser2", canBeEmpty, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sshUsers := strings.Split(users, ",")
|
||||
|
||||
// Prompt the user by prompts defined by the provider
|
||||
r := []events.YAMLPrompt{}
|
||||
|
||||
bus.Manager.Response(events.EventInteractiveInstall, func(p *pluggable.Plugin, resp *pluggable.EventResponse) {
|
||||
err := json.Unmarshal([]byte(resp.Data), &r)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
|
||||
_, err = bus.Manager.Publish(events.EventInteractiveInstall, events.EventPayload{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unstructuredYAML := map[string]interface{}{}
|
||||
for _, p := range r {
|
||||
unstructuredYAML, err = promptToUnstructured(p, unstructuredYAML)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
result, err := unstructured.ToYAMLMap(unstructuredYAML)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
allGood, err := prompt("Are settings ok?", "n", yesNo, true, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !isYes(allGood) {
|
||||
return InteractiveInstall(spawnShell)
|
||||
}
|
||||
|
||||
c := &config.Config{
|
||||
Install: &config.Install{
|
||||
Device: device,
|
||||
},
|
||||
}
|
||||
|
||||
usersToSet := map[string]schema.User{}
|
||||
|
||||
if userName != "" {
|
||||
user := schema.User{
|
||||
Name: userName,
|
||||
PasswordHash: userPassword,
|
||||
Groups: []string{"admin"},
|
||||
SSHAuthorizedKeys: sshUsers,
|
||||
}
|
||||
|
||||
usersToSet = map[string]schema.User{
|
||||
userName: user,
|
||||
}
|
||||
}
|
||||
|
||||
cloudConfig := schema.YipConfig{Name: "Config generated by the installer",
|
||||
Stages: map[string][]schema.Stage{config.NetworkStage.String(): {
|
||||
{
|
||||
Users: usersToSet,
|
||||
},
|
||||
}}}
|
||||
|
||||
dat, err := config.MergeYAML(cloudConfig, c, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finalCloudConfig := config.AddHeader("#cloud-config", string(dat))
|
||||
|
||||
pterm.Info.Println("Starting installation")
|
||||
pterm.Info.Println(finalCloudConfig)
|
||||
|
||||
err = RunInstall(map[string]string{
|
||||
"device": device,
|
||||
"cc": finalCloudConfig,
|
||||
})
|
||||
if err != nil {
|
||||
pterm.Error.Println(err.Error())
|
||||
}
|
||||
|
||||
if spawnShell {
|
||||
return utils.Shell().Run()
|
||||
}
|
||||
return err
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "${GOPATH}" ]; then
|
||||
echo GOPATH environment variable not set
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -e "${GOPATH}/bin/2goarray" ]; then
|
||||
echo "Installing 2goarray..."
|
||||
if ! go get github.com/cratonica/2goarray; then
|
||||
echo Failure executing go get github.com/cratonica/2goarray
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo Please specify a PNG file
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ! -f "$1" ]; then
|
||||
echo "${1} is not a valid file"
|
||||
exit
|
||||
fi
|
||||
|
||||
OUTPUT=iconunix.go
|
||||
echo "Generating ${OUTPUT}"
|
||||
echo "//+build linux darwin" > "${OUTPUT}"
|
||||
echo >> "${OUTPUT}"
|
||||
if ! "${GOPATH}"/bin/2goarray DefaultBanner agent < "${1}" >> "${OUTPUT}"; then
|
||||
echo Failure generating "${OUTPUT}"
|
||||
exit
|
||||
fi
|
||||
echo Finished
|
@ -1,34 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
func Notify(event string, dirs []string) error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !events.IsEventDefined(event) {
|
||||
return fmt.Errorf("event '%s' not defined", event)
|
||||
}
|
||||
|
||||
configStr, err := c.String()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bus.Manager.Publish(pluggable.EventType(event), events.EventPayload{
|
||||
Config: configStr,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
qr "github.com/mudler/go-nodepair/qrcode"
|
||||
"github.com/mudler/go-pluggable"
|
||||
"github.com/pterm/pterm"
|
||||
)
|
||||
|
||||
func Recovery() error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
token := ""
|
||||
msg := ""
|
||||
busErr := ""
|
||||
|
||||
bus.Manager.Response(events.EventRecovery, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
token = r.Data
|
||||
msg = r.State
|
||||
busErr = r.Error
|
||||
})
|
||||
|
||||
cmd.PrintBranding(DefaultBanner)
|
||||
|
||||
agentConfig, err := LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PrintText(agentConfig.Branding.Recovery, "Recovery")
|
||||
|
||||
_, err = bus.Manager.Publish(events.EventRecovery, events.EventPayload{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if busErr != "" {
|
||||
return fmt.Errorf(busErr)
|
||||
}
|
||||
|
||||
if !agentConfig.Fast {
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
pterm.Info.Println(msg)
|
||||
|
||||
if token != "" {
|
||||
qr.Print(token)
|
||||
}
|
||||
|
||||
// Wait for user input and go back to shell
|
||||
utils.Prompt("") //nolint:errcheck
|
||||
_, err = bus.Manager.Publish(events.EventRecoveryStop, events.EventPayload{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// give tty1 back
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint:errcheck
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sdk "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
hook "github.com/kairos-io/kairos/v2/internal/agent/hooks"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/internal/cmd"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
|
||||
"github.com/mudler/go-pluggable"
|
||||
"github.com/pterm/pterm"
|
||||
)
|
||||
|
||||
func Reset(dir ...string) error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
options := map[string]string{}
|
||||
|
||||
bus.Manager.Response(sdk.EventBeforeReset, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
err := json.Unmarshal([]byte(r.Data), &options)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
})
|
||||
|
||||
cmd.PrintBranding(DefaultBanner)
|
||||
|
||||
agentConfig, err := LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.PrintText(agentConfig.Branding.Reset, "Reset")
|
||||
|
||||
// We don't close the lock, as none of the following actions are expected to return
|
||||
lock := sync.Mutex{}
|
||||
go func() {
|
||||
// Wait for user input and go back to shell
|
||||
utils.Prompt("") //nolint:errcheck
|
||||
// give tty1 back
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint:errcheck
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
fmt.Println("Reset aborted")
|
||||
panic(utils.Shell().Run())
|
||||
}()
|
||||
|
||||
if !agentConfig.Fast {
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
lock.Lock()
|
||||
args := []string{"reset"}
|
||||
|
||||
ensureDataSourceReady()
|
||||
|
||||
bus.Manager.Publish(sdk.EventBeforeReset, sdk.EventPayload{}) //nolint:errcheck
|
||||
|
||||
optsArgs := optsToArgs(options)
|
||||
if len(optsArgs) > 0 {
|
||||
args = append(args, optsArgs...)
|
||||
} else {
|
||||
args = append(args, "--reset-persistent")
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dir...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
cmd := exec.Command("elemental", args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := hook.Run(*c, hook.AfterReset...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bus.Manager.Publish(sdk.EventAfterReset, sdk.EventPayload{}) //nolint:errcheck
|
||||
|
||||
if !agentConfig.Fast {
|
||||
pterm.Info.Println("Rebooting in 60 seconds, press Enter to abort...")
|
||||
}
|
||||
|
||||
// We don't close the lock, as none of the following actions are expected to return
|
||||
lock2 := sync.Mutex{}
|
||||
go func() {
|
||||
// Wait for user input and go back to shell
|
||||
utils.Prompt("") //nolint:errcheck
|
||||
// give tty1 back
|
||||
svc, err := machine.Getty(1)
|
||||
if err == nil {
|
||||
svc.Start() //nolint:errcheck
|
||||
}
|
||||
|
||||
lock2.Lock()
|
||||
fmt.Println("Reboot aborted")
|
||||
panic(utils.Shell().Run())
|
||||
}()
|
||||
|
||||
if !agentConfig.Fast {
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
lock2.Lock()
|
||||
utils.Reboot()
|
||||
|
||||
return nil
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/kairos-io/kairos/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
"github.com/kairos-io/kairos/v2/pkg/github"
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
func ListReleases(includePrereleases bool) semver.Collection {
|
||||
var releases semver.Collection
|
||||
|
||||
bus.Manager.Response(events.EventAvailableReleases, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
if err := json.Unmarshal([]byte(r.Data), &releases); err != nil {
|
||||
fmt.Printf("warn: failed unmarshalling data: '%s'\n", err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
if _, err := bus.Manager.Publish(events.EventAvailableReleases, events.EventPayload{}); err != nil {
|
||||
fmt.Printf("warn: failed publishing event: '%s'\n", err.Error())
|
||||
}
|
||||
|
||||
if len(releases) == 0 {
|
||||
githubRepo, err := utils.OSRelease("GITHUB_REPO")
|
||||
if err != nil {
|
||||
return releases
|
||||
}
|
||||
fmt.Println("Searching for releases")
|
||||
if includePrereleases {
|
||||
fmt.Println("Including pre-releases")
|
||||
}
|
||||
releases, _ = github.FindReleases(context.Background(), "", githubRepo, includePrereleases)
|
||||
}
|
||||
|
||||
return releases
|
||||
}
|
||||
|
||||
func Upgrade(
|
||||
version, image string, force, debug, strictValidations bool, dirs []string, authUser string,
|
||||
authPass string, authServer string, authType string, registryToken string, identityToken string, preReleases bool,
|
||||
) error {
|
||||
bus.Manager.Initialize()
|
||||
|
||||
if version == "" && image == "" {
|
||||
fmt.Println("Searching for releases")
|
||||
if preReleases {
|
||||
fmt.Println("Including pre-releases")
|
||||
}
|
||||
releases := ListReleases(preReleases)
|
||||
|
||||
if len(releases) == 0 {
|
||||
return fmt.Errorf("no releases found")
|
||||
}
|
||||
|
||||
// Using Original here because the parsing removes the v as its a semver. But it stores the original full version there
|
||||
version = releases[0].Original()
|
||||
|
||||
if utils.Version() == version && !force {
|
||||
fmt.Printf("version %s already installed. use --force to force upgrade\n", version)
|
||||
return nil
|
||||
}
|
||||
msg := fmt.Sprintf("Latest release is %s\nAre you sure you want to upgrade to this release? (y/n)", version)
|
||||
reply, err := promptBool(events.YAMLPrompt{Prompt: msg, Default: "y"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if reply == "false" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
discoveredImage := ""
|
||||
bus.Manager.Response(events.EventVersionImage, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
discoveredImage = r.Data
|
||||
})
|
||||
|
||||
_, err := bus.Manager.Publish(events.EventVersionImage, &events.VersionImagePayload{
|
||||
Version: version,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registry, err := utils.OSRelease("IMAGE_REPO")
|
||||
if err != nil {
|
||||
fmt.Printf("Cant find IMAGE_REPO key under /etc/os-release\n")
|
||||
return err
|
||||
}
|
||||
|
||||
img := fmt.Sprintf("%s:%s", registry, version)
|
||||
if discoveredImage != "" {
|
||||
img = discoveredImage
|
||||
}
|
||||
if image != "" {
|
||||
img = image
|
||||
}
|
||||
|
||||
if debug {
|
||||
fmt.Printf("Upgrading to image: '%s'\n", img)
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...), collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
args := []string{"upgrade", "--system.uri", fmt.Sprintf("docker:%s", img)}
|
||||
args = append(args,
|
||||
"--auth-username", authUser,
|
||||
"--auth-password", authPass,
|
||||
"--auth-server-address", authServer,
|
||||
"--auth-type", authType,
|
||||
"--auth-registry-token", registryToken,
|
||||
"--auth-identity-token", identityToken,
|
||||
)
|
||||
|
||||
if debug {
|
||||
fmt.Printf("Running command: 'elemental %s'", strings.Join(args, " "))
|
||||
}
|
||||
|
||||
cmd := exec.Command("elemental", args...)
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
sc "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
)
|
||||
|
||||
// JSONSchema builds a JSON Schema based on the Root Schema and the given version
|
||||
// this is helpful when mapping a validation error.
|
||||
func JSONSchema(version string) (string, error) {
|
||||
url := fmt.Sprintf("https://kairos.io/%s/cloud-config.json", version)
|
||||
schema, err := sc.GenerateSchema(sc.RootSchema{}, url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
// Validate ensures that a given schema is Valid according to the Root Schema from the agent.
|
||||
func Validate(source string) error {
|
||||
var yaml string
|
||||
|
||||
if strings.HasPrefix(source, "http") {
|
||||
resp, err := http.Get(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//Convert the body to type string
|
||||
yaml = string(body)
|
||||
} else {
|
||||
// Maybe we should just try to read the string for the normal headers? That would identify a full yaml vs a file
|
||||
dat, err := os.ReadFile(source)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no such file or directory") || strings.Contains(err.Error(), "file name too long") {
|
||||
yaml = source
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
yaml = string(dat)
|
||||
}
|
||||
}
|
||||
|
||||
config, err := sc.NewConfigFromYAML(yaml, sc.RootSchema{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !config.HasHeader() {
|
||||
return fmt.Errorf("missing #cloud-config header")
|
||||
}
|
||||
|
||||
if config.IsValid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = config.ValidationError
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
package agent_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/internal/agent"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Validate", func() {
|
||||
Context("JSONSchema", func() {
|
||||
It("returns a schema with a url to the given version", func() {
|
||||
out, err := JSONSchema("0.0.0")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(strings.Contains(out, `$schema": "https://kairos.io/0.0.0/cloud-config.json"`)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Validate", func() {
|
||||
var yaml string
|
||||
|
||||
Context("With a really long config string", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
users:
|
||||
- name: kairos
|
||||
passwd: kairos
|
||||
vpn:
|
||||
network_token: "dssdnfjkldashfkjhasdkhfkasjdhfkjhasdjkfhaksjdhfkjashjdkfhioreqwhfuihqweruifhuewrbfhuewrfuyequfhuiehuifheqrihfuiqrehfuirqheiufhreqiuhfuiqheiufhqeuihfuiqrehfiuhqreuifrhiuqehfiuhqeirhfiuewhrfhqwehfriuewhfuihewiuhfruewhrifhwiuehrfiuhweiurfhwueihrfuiwehufhweuihrfuiwerhfuihewruifhewuihfiouwehrfiouhwei"
|
||||
`
|
||||
})
|
||||
It("validates", func() {
|
||||
Expect(Validate(yaml)).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with a valid config", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
users:
|
||||
- name: kairos
|
||||
passwd: kairos`
|
||||
})
|
||||
|
||||
It("is successful reading it from file", func() {
|
||||
f, err := ioutil.TempDir("", "tests")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(f)
|
||||
|
||||
path := filepath.Join(f, "config.yaml")
|
||||
err = os.WriteFile(path, []byte(yaml), 0655)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = Validate(path)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("is successful reading it from a string", func() {
|
||||
Expect(Validate(yaml)).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("without a header", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `users:
|
||||
- name: kairos
|
||||
passwd: kairos`
|
||||
})
|
||||
|
||||
It("is fails", func() {
|
||||
f, err := ioutil.TempDir("", "tests")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(f)
|
||||
|
||||
path := filepath.Join(f, "config.yaml")
|
||||
err = os.WriteFile(path, []byte(yaml), 0655)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = Validate(path)
|
||||
Expect(err).To(MatchError("missing #cloud-config header"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("with an invalid rule", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
users:
|
||||
- name: 007
|
||||
passwd: kairos`
|
||||
})
|
||||
|
||||
It("is fails", func() {
|
||||
f, err := ioutil.TempDir("", "tests")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
defer os.RemoveAll(f)
|
||||
|
||||
path := filepath.Join(f, "config.yaml")
|
||||
err = os.WriteFile(path, []byte(yaml), 0655)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = Validate(path)
|
||||
Expect(err.Error()).To(MatchRegexp("expected string, but got number"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -1,73 +0,0 @@
|
||||
package bus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bus"
|
||||
|
||||
"github.com/mudler/go-pluggable"
|
||||
)
|
||||
|
||||
// Manager is the bus instance manager, which subscribes plugins to events emitted.
|
||||
var Manager = NewBus()
|
||||
|
||||
func NewBus() *Bus {
|
||||
return &Bus{
|
||||
Manager: pluggable.NewManager(
|
||||
bus.AllEvents,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func Reload() {
|
||||
Manager = NewBus()
|
||||
Manager.Initialize()
|
||||
}
|
||||
|
||||
type Bus struct {
|
||||
*pluggable.Manager
|
||||
registered bool
|
||||
}
|
||||
|
||||
func (b *Bus) LoadProviders() {
|
||||
wd, _ := os.Getwd()
|
||||
b.Manager.Autoload("agent-provider", "/system/providers", "/usr/local/system/providers", wd).Register()
|
||||
}
|
||||
|
||||
func (b *Bus) HasRegisteredPlugins() bool {
|
||||
return len(b.Plugins) > 0
|
||||
}
|
||||
|
||||
func (b *Bus) Initialize() {
|
||||
if b.registered {
|
||||
return
|
||||
}
|
||||
|
||||
b.LoadProviders()
|
||||
for i := range b.Manager.Events {
|
||||
e := b.Manager.Events[i]
|
||||
b.Manager.Response(e, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
if os.Getenv("BUS_DEBUG") == "true" {
|
||||
fmt.Println(
|
||||
fmt.Sprintf("[provider event: %s]", e),
|
||||
"received from",
|
||||
p.Name,
|
||||
"at",
|
||||
p.Executable,
|
||||
r,
|
||||
)
|
||||
}
|
||||
if r.Errored() {
|
||||
err := fmt.Sprintf("Provider %s at %s had an error: %s", p.Name, p.Executable, r.Error)
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if r.State != "" {
|
||||
fmt.Println(fmt.Sprintf("[provider event: %s]", e), r.State)
|
||||
}
|
||||
})
|
||||
}
|
||||
b.registered = true
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/kairos"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
"github.com/pterm/pterm"
|
||||
)
|
||||
|
||||
func PrintText(f string, banner string) {
|
||||
pterm.DefaultBox.WithTitle(banner).WithTitleBottomRight().WithRightPadding(0).WithBottomPadding(0).Println(
|
||||
f)
|
||||
}
|
||||
|
||||
func ClearScreen() {
|
||||
fmt.Print("\033c")
|
||||
}
|
||||
|
||||
func PrintBranding(b []byte) {
|
||||
brandingFile := kairos.BrandingFile("banner")
|
||||
if _, err := os.Stat(brandingFile); err == nil {
|
||||
f, err := os.ReadFile(brandingFile)
|
||||
if err == nil {
|
||||
fmt.Println(string(f))
|
||||
return
|
||||
}
|
||||
}
|
||||
utils.PrintBanner(b)
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package common
|
||||
|
||||
var VERSION = "0.0.0"
|
@ -1,7 +0,0 @@
|
||||
package kairos
|
||||
|
||||
import "path"
|
||||
|
||||
func BrandingFile(s string) string {
|
||||
return path.Join("/etc", "kairos", "branding", s)
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
# Web UI
|
||||
|
||||
## Dependencies
|
||||
|
||||
All dependencies are defined in the package.json and package-lock.json files.
|
||||
You can use `npm` to install or upgrade any packages.
|
@ -1,8 +0,0 @@
|
||||
const { defineConfig } = require("cypress");
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
supportFile: false,
|
||||
baseUrl: "http://localhost:8080"
|
||||
},
|
||||
});
|
@ -1,49 +0,0 @@
|
||||
describe('Basic Tests for webui', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/')
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
url: '/validate',
|
||||
}, {log: false}).as('validate')
|
||||
})
|
||||
it("basic items on the ui exist", () => {
|
||||
cy.contains('#cloud-config').should("exist").should("be.visible")
|
||||
cy.contains('Welcome to the Installer!').should("exist").should("be.visible")
|
||||
cy.contains("p a", "cloud-config config configuration file")
|
||||
.should("have.attr", "href", "/local/docs/reference/configuration/")
|
||||
cy.get("#cloud-config-help a")
|
||||
.should("have.attr", "href", "/local/docs/examples/")
|
||||
cy.get("#installation-device").should("have.value", "auto")
|
||||
|
||||
// footer
|
||||
cy.get("a .fa-github").should("exist").parent().should("have.attr", "href", "https://github.com/kairos-io/kairos")
|
||||
cy.get("a .fa-book").should("exist").parent().should("have.attr", "href", "https://kairos.io/docs")
|
||||
cy.get("#reboot-checkbox").should("exist").should("not.be.checked")
|
||||
cy.get("#poweroff-checkbox").should("exist").should("not.be.checked")
|
||||
cy.get("button").should("exist").invoke("text").should("equal", "Install")
|
||||
})
|
||||
it('validation works', () => {
|
||||
cy.get('.CodeMirror')
|
||||
.first()
|
||||
.then((editor) => {
|
||||
editor[0].CodeMirror.setValue('');
|
||||
});
|
||||
|
||||
cy.get(".CodeMirror textarea").type("#cloud-config{enter}users:{enter} - name: itxaka", {force: true})
|
||||
cy.get("#validator-alert").should("have.text", "Valid YAML syntax")
|
||||
|
||||
})
|
||||
it('validation fails ', () => {
|
||||
cy.get('.CodeMirror')
|
||||
.first()
|
||||
.then((editor) => {
|
||||
editor[0].CodeMirror.setValue('');
|
||||
});
|
||||
cy.get(".CodeMirror textarea").type("blablabla", {force: true})
|
||||
cy.get("#validator-alert").invoke("text").should("match", /Failed validating syntax/)
|
||||
|
||||
})
|
||||
it('should install', function () {
|
||||
cy.get("button").click()
|
||||
});
|
||||
})
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
3516
internal/webui/public/package-lock.json
generated
3516
internal/webui/public/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.2.1",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"alpinejs": "^3.10.5",
|
||||
"bootstrap": "^5.0.0",
|
||||
"codemirror": "^5.57.0",
|
||||
"jquery": "^3.5.1",
|
||||
"xterm": "^5.1.0",
|
||||
"xterm-addon-fit": "^0.7.0",
|
||||
"xterm-theme": "^1.1.0",
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^12.9.0"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -1,256 +0,0 @@
|
||||
package webui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/internal/agent"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"github.com/labstack/echo/v4"
|
||||
process "github.com/mudler/go-processmanager"
|
||||
"github.com/nxadm/tail"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
type FormData struct {
|
||||
CloudConfig string `form:"cloud-config" json:"cloud-config" query:"cloud-config"`
|
||||
Reboot string `form:"reboot" json:"reboot" query:"reboot"`
|
||||
|
||||
PowerOff string `form:"power-off" json:"power-off" query:"power-off"`
|
||||
InstallationDevice string `form:"installation-device" json:"installation-device" query:"installation-device"`
|
||||
}
|
||||
|
||||
//go:embed public
|
||||
var embededFiles embed.FS
|
||||
|
||||
func getFileSystem() http.FileSystem {
|
||||
fsys, err := fs.Sub(embededFiles, "public")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return http.FS(fsys)
|
||||
}
|
||||
|
||||
func getFS() fs.FS {
|
||||
fsys, err := fs.Sub(embededFiles, "public")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return fsys
|
||||
}
|
||||
|
||||
func streamProcess(s *state) func(c echo.Context) error {
|
||||
return func(c echo.Context) error {
|
||||
consumeError := func(err error) {
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
}
|
||||
websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer ws.Close()
|
||||
for {
|
||||
s.Lock()
|
||||
if s.p == nil {
|
||||
// Write
|
||||
err := websocket.Message.Send(ws, "No process!")
|
||||
consumeError(err)
|
||||
s.Unlock()
|
||||
return
|
||||
}
|
||||
s.Unlock()
|
||||
|
||||
if !s.p.IsAlive() {
|
||||
errOut, err := os.ReadFile(s.p.StderrPath())
|
||||
if err == nil {
|
||||
err := websocket.Message.Send(ws, string(errOut))
|
||||
consumeError(err)
|
||||
}
|
||||
out, err := os.ReadFile(s.p.StdoutPath())
|
||||
if err == nil {
|
||||
err = websocket.Message.Send(ws, string(out))
|
||||
consumeError(err)
|
||||
}
|
||||
err = websocket.Message.Send(ws, "Process stopped!")
|
||||
consumeError(err)
|
||||
return
|
||||
}
|
||||
|
||||
t, err := tail.TailFile(s.p.StdoutPath(), tail.Config{Follow: true})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t2, err := tail.TailFile(s.p.StderrPath(), tail.Config{Follow: true})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case line := <-t.Lines:
|
||||
err = websocket.Message.Send(ws, line.Text+"\r\n")
|
||||
consumeError(err)
|
||||
case line := <-t2.Lines:
|
||||
err = websocket.Message.Send(ws, line.Text+"\r\n")
|
||||
consumeError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).ServeHTTP(c.Response(), c.Request())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type state struct {
|
||||
p *process.Process
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// TemplateRenderer is a custom html/template renderer for Echo framework.
|
||||
type TemplateRenderer struct {
|
||||
templates *template.Template
|
||||
}
|
||||
|
||||
// Render renders a template document.
|
||||
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
|
||||
|
||||
// Add global methods if data is a map
|
||||
if viewContext, isMap := data.(map[string]interface{}); isMap {
|
||||
viewContext["reverse"] = c.Echo().Reverse
|
||||
}
|
||||
|
||||
return t.templates.ExecuteTemplate(w, name, data)
|
||||
}
|
||||
|
||||
func Start(ctx context.Context) error {
|
||||
|
||||
s := state{}
|
||||
listen := config.DefaultWebUIListenAddress
|
||||
|
||||
ec := echo.New()
|
||||
assetHandler := http.FileServer(getFileSystem())
|
||||
|
||||
renderer := &TemplateRenderer{
|
||||
templates: template.Must(template.ParseFS(getFS(), "*.html")),
|
||||
}
|
||||
|
||||
ec.Renderer = renderer
|
||||
agentConfig, err := agent.LoadConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if agentConfig.WebUI.ListenAddress != "" {
|
||||
listen = agentConfig.WebUI.ListenAddress
|
||||
}
|
||||
|
||||
if agentConfig.WebUI.Disable {
|
||||
log.Println("WebUI installer disabled by branding")
|
||||
return nil
|
||||
}
|
||||
|
||||
ec.GET("/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
|
||||
|
||||
ec.POST("/validate", func(c echo.Context) error {
|
||||
formData := new(FormData)
|
||||
if err := c.Bind(formData); err != nil {
|
||||
return err
|
||||
}
|
||||
cloudConfig := formData.CloudConfig
|
||||
|
||||
err := agent.Validate(cloudConfig)
|
||||
if err != nil {
|
||||
return c.String(http.StatusOK, err.Error())
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
})
|
||||
|
||||
ec.POST("/install", func(c echo.Context) error {
|
||||
|
||||
s.Lock()
|
||||
if s.p != nil {
|
||||
status, _ := s.p.ExitCode()
|
||||
if s.p.IsAlive() || status == "0" {
|
||||
s.Unlock()
|
||||
return c.Redirect(http.StatusSeeOther, "progress.html")
|
||||
}
|
||||
}
|
||||
s.Unlock()
|
||||
|
||||
formData := new(FormData)
|
||||
if err := c.Bind(formData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process the form data as necessary
|
||||
cloudConfig := formData.CloudConfig
|
||||
reboot := formData.Reboot
|
||||
powerOff := formData.PowerOff
|
||||
installationDevice := formData.InstallationDevice
|
||||
|
||||
args := []string{"manual-install"}
|
||||
|
||||
if powerOff == "on" {
|
||||
args = append(args, "--poweroff")
|
||||
}
|
||||
if reboot == "on" {
|
||||
args = append(args, "--reboot")
|
||||
}
|
||||
args = append(args, "--device", installationDevice)
|
||||
|
||||
// create tempfile to store cloud-config, bail out if we fail as we couldn't go much further
|
||||
file, err := os.CreateTemp("", "install-webui-*.yaml")
|
||||
if err != nil {
|
||||
log.Fatalf("could not create tmpfile for cloud-config: %s", err.Error())
|
||||
}
|
||||
|
||||
err = os.WriteFile(file.Name(), []byte(cloudConfig), 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("could not write tmpfile for cloud-config: %s", err.Error())
|
||||
}
|
||||
|
||||
args = append(args, file.Name())
|
||||
|
||||
s.Lock()
|
||||
s.p = process.New(process.WithName("/usr/bin/kairos-agent"), process.WithArgs(args...), process.WithTemporaryStateDir())
|
||||
s.Unlock()
|
||||
err = s.p.Run()
|
||||
if err != nil {
|
||||
return c.Render(http.StatusOK, "message.html", map[string]interface{}{
|
||||
"message": err.Error(),
|
||||
"type": "danger",
|
||||
})
|
||||
}
|
||||
|
||||
// Start install process, lock with sentinel
|
||||
return c.Redirect(http.StatusSeeOther, "progress.html")
|
||||
})
|
||||
|
||||
ec.GET("/ws", streamProcess(&s))
|
||||
|
||||
if err := ec.Start(listen); err != nil && err != http.ErrServerClosed {
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
ct, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
err := ec.Shutdown(ct)
|
||||
if err != nil {
|
||||
log.Printf("shutdown failed: %s", err.Error())
|
||||
}
|
||||
cancel()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
@ -1,471 +0,0 @@
|
||||
// Package configcollector can be used to merge configuration from different
|
||||
// sources into one YAML.
|
||||
package collector
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/machine"
|
||||
|
||||
"github.com/avast/retry-go"
|
||||
"github.com/itchyny/gojq"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const DefaultHeader = "#cloud-config"
|
||||
|
||||
var ValidFileHeaders = []string{
|
||||
"#cloud-config",
|
||||
"#kairos-config",
|
||||
"#node-config",
|
||||
}
|
||||
|
||||
type Configs []*Config
|
||||
|
||||
// We don't allow yamls that are plain arrays because is has no use in Kairos
|
||||
// and there is no way to merge an array yaml with a "map" yaml.
|
||||
type Config map[string]interface{}
|
||||
|
||||
// MergeConfigURL looks for the "config_url" key and if it's found
|
||||
// it downloads the remote config and merges it with the current one.
|
||||
// If the remote config also has config_url defined, it is also fetched
|
||||
// recursively until a remote config no longer defines a config_url.
|
||||
// NOTE: The "config_url" value of the final result is the value of the last
|
||||
// config file in the chain because we replace values when we merge.
|
||||
func (c *Config) MergeConfigURL() error {
|
||||
// If there is no config_url, just return (do nothing)
|
||||
configURL := c.ConfigURL()
|
||||
if configURL == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetch the remote config
|
||||
remoteConfig, err := fetchRemoteConfig(configURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// recursively fetch remote configs
|
||||
if err := remoteConfig.MergeConfigURL(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// merge remoteConfig back to "c"
|
||||
return c.MergeConfig(remoteConfig)
|
||||
}
|
||||
|
||||
func (c *Config) toMap() (map[string]interface{}, error) {
|
||||
var result map[string]interface{}
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *Config) applyMap(i interface{}) error {
|
||||
data, err := yaml.Marshal(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(data, c)
|
||||
return err
|
||||
}
|
||||
|
||||
// MergeConfig merges the config passed as parameter back to the receiver Config.
|
||||
func (c *Config) MergeConfig(newConfig *Config) error {
|
||||
var err error
|
||||
|
||||
// convert the two configs into maps
|
||||
aMap, err := c.toMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bMap, err := newConfig.toMap()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// deep merge the two maps
|
||||
cMap, err := DeepMerge(aMap, bMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// apply the result of the deepmerge into the base config
|
||||
return c.applyMap(cMap)
|
||||
}
|
||||
|
||||
func deepMergeSlices(sliceA, sliceB []interface{}) ([]interface{}, error) {
|
||||
// We use the first item in the slice to determine if there are maps present.
|
||||
// Do we need to do the same for other types?
|
||||
firstItem := sliceA[0]
|
||||
if reflect.ValueOf(firstItem).Kind() == reflect.Map {
|
||||
temp := make(map[string]interface{})
|
||||
|
||||
// first we put in temp all the keys present in a, and assign them their existing values
|
||||
for _, item := range sliceA {
|
||||
for k, v := range item.(map[string]interface{}) {
|
||||
temp[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// then we go through b to merge each of its keys
|
||||
for _, item := range sliceB {
|
||||
for k, v := range item.(map[string]interface{}) {
|
||||
current, ok := temp[k]
|
||||
if ok {
|
||||
// if the key exists, we deep merge it
|
||||
dm, err := DeepMerge(current, v)
|
||||
if err != nil {
|
||||
return []interface{}{}, fmt.Errorf("cannot merge %s with %s", current, v)
|
||||
}
|
||||
temp[k] = dm
|
||||
} else {
|
||||
// otherwise we just set it
|
||||
temp[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []interface{}{temp}, nil
|
||||
}
|
||||
|
||||
// for simple slices
|
||||
for _, v := range sliceB {
|
||||
i := slices.Index(sliceA, v)
|
||||
if i < 0 {
|
||||
sliceA = append(sliceA, v)
|
||||
}
|
||||
}
|
||||
|
||||
return sliceA, nil
|
||||
}
|
||||
|
||||
func deepMergeMaps(a, b map[string]interface{}) (map[string]interface{}, error) {
|
||||
// go through all items in b and merge them to a
|
||||
for k, v := range b {
|
||||
current, ok := a[k]
|
||||
if ok {
|
||||
// when the key is already set, we don't know what type it has, so we deep merge them in case they are maps
|
||||
// or slices
|
||||
res, err := DeepMerge(current, v)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
a[k] = res
|
||||
} else {
|
||||
a[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// DeepMerge takes two data structures and merges them together deeply. The results can vary depending on how the
|
||||
// arguments are passed since structure B will always overwrite what's on A.
|
||||
func DeepMerge(a, b interface{}) (interface{}, error) {
|
||||
if a == nil && b != nil {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
typeA := reflect.TypeOf(a)
|
||||
typeB := reflect.TypeOf(b)
|
||||
|
||||
// We don't support merging different data structures
|
||||
if typeA.Kind() != typeB.Kind() {
|
||||
return map[string]interface{}{}, fmt.Errorf("cannot merge %s with %s", typeA.String(), typeB.String())
|
||||
}
|
||||
|
||||
if typeA.Kind() == reflect.Slice {
|
||||
return deepMergeSlices(a.([]interface{}), b.([]interface{}))
|
||||
}
|
||||
|
||||
if typeA.Kind() == reflect.Map {
|
||||
return deepMergeMaps(a.(map[string]interface{}), b.(map[string]interface{}))
|
||||
}
|
||||
|
||||
// for any other type, b should take precedence
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// String returns a string which is a Yaml representation of the Config.
|
||||
func (c *Config) String() (string, error) {
|
||||
data, err := yaml.Marshal(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n\n%s", DefaultHeader, string(data)), nil
|
||||
}
|
||||
|
||||
func (cs Configs) Merge() (*Config, error) {
|
||||
result := &Config{}
|
||||
|
||||
for _, c := range cs {
|
||||
if err := c.MergeConfigURL(); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
if err := result.MergeConfig(c); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func Scan(o *Options, filter func(d []byte) ([]byte, error)) (*Config, error) {
|
||||
configs := Configs{}
|
||||
|
||||
configs = append(configs, parseFiles(o.ScanDir, o.NoLogs)...)
|
||||
|
||||
if o.MergeBootCMDLine {
|
||||
cConfig, err := ParseCmdLine(o.BootCMDLineFile, filter)
|
||||
o.SoftErr("parsing cmdline", err)
|
||||
if err == nil { // best-effort
|
||||
configs = append(configs, cConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return configs.Merge()
|
||||
}
|
||||
|
||||
func allFiles(dir []string) []string {
|
||||
files := []string{}
|
||||
for _, d := range dir {
|
||||
if f, err := listFiles(d); err == nil {
|
||||
files = append(files, f...)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
// parseFiles returns a list of Configs parsed from files.
|
||||
func parseFiles(dir []string, nologs bool) Configs {
|
||||
result := Configs{}
|
||||
files := allFiles(dir)
|
||||
for _, f := range files {
|
||||
if fileSize(f) > 1.0 {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s. too big (>1MB)\n", f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if strings.Contains(f, "userdata") || filepath.Ext(f) == ".yml" || filepath.Ext(f) == ".yaml" {
|
||||
b, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s. %s\n", f, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if !HasValidHeader(string(b)) {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s because it has no valid header\n", f)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
var newConfig Config
|
||||
err = yaml.Unmarshal(b, &newConfig)
|
||||
if err != nil && !nologs {
|
||||
fmt.Printf("warning: failed to parse config:\n%s\n", err.Error())
|
||||
}
|
||||
result = append(result, &newConfig)
|
||||
} else {
|
||||
if !nologs {
|
||||
fmt.Printf("warning: skipping %s (extension).\n", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func fileSize(f string) float64 {
|
||||
file, err := os.Open(f)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
bytes := stat.Size()
|
||||
kilobytes := (bytes / 1024)
|
||||
megabytes := (float64)(kilobytes / 1024) // cast to type float64
|
||||
|
||||
return megabytes
|
||||
}
|
||||
|
||||
func listFiles(dir string) ([]string, error) {
|
||||
content := []string{}
|
||||
|
||||
err := filepath.Walk(dir,
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
content = append(content, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return content, err
|
||||
}
|
||||
|
||||
// ParseCmdLine reads options from the kernel cmdline and returns the equivalent
|
||||
// Config.
|
||||
func ParseCmdLine(file string, filter func(d []byte) ([]byte, error)) (*Config, error) {
|
||||
result := Config{}
|
||||
dotToYAML, err := machine.DotToYAML(file)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
filteredYAML, err := filter(dotToYAML)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(filteredYAML, &result)
|
||||
if err != nil {
|
||||
return &result, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// ConfigURL returns the value of config_url if set or empty string otherwise.
|
||||
func (c Config) ConfigURL() string {
|
||||
if val, hasKey := c["config_url"]; hasKey {
|
||||
if s, isString := val.(string); isString {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func fetchRemoteConfig(url string) (*Config, error) {
|
||||
var body []byte
|
||||
result := &Config{}
|
||||
|
||||
err := retry.Do(
|
||||
func() error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("unexpected status: %d", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, retry.Delay(time.Second), retry.Attempts(3),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
// TODO: improve logging
|
||||
fmt.Printf("WARNING: Couldn't fetch config_url: %s", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if !HasValidHeader(string(body)) {
|
||||
// TODO: Print a warning when we implement proper logging
|
||||
fmt.Println("No valid header in remote config: %w", err)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(body, result); err != nil {
|
||||
return result, fmt.Errorf("could not unmarshal remote config to an object: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func HasValidHeader(data string) bool {
|
||||
header := strings.SplitN(data, "\n", 2)[0]
|
||||
|
||||
// Trim trailing whitespaces
|
||||
header = strings.TrimRightFunc(header, unicode.IsSpace)
|
||||
|
||||
// NOTE: we also allow "legacy" headers. Should only allow #cloud-config at
|
||||
// some point.
|
||||
return (header == DefaultHeader) || (header == "#kairos-config") || (header == "#node-config")
|
||||
}
|
||||
|
||||
func (c Config) Query(s string) (res string, err error) {
|
||||
s = fmt.Sprintf(".%s", s)
|
||||
|
||||
var dat map[string]interface{}
|
||||
var dat1 map[string]interface{}
|
||||
|
||||
yamlStr, err := c.String()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Marshall it so it removes the first line which cannot be parsed
|
||||
err = yaml.Unmarshal([]byte(yamlStr), &dat1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// Transform it to json so its parsed correctly by gojq
|
||||
b, err := json.Marshal(dat1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := json.Unmarshal(b, &dat); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
query, err := gojq.Parse(s)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
iter := query.Run(dat) // or query.RunWithContext
|
||||
for {
|
||||
v, ok := iter.Next()
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
if err, ok := v.(error); ok {
|
||||
return res, fmt.Errorf("failed parsing, error: %w", err)
|
||||
}
|
||||
|
||||
dat, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
res += string(dat)
|
||||
}
|
||||
return
|
||||
}
|
@ -1,754 +0,0 @@
|
||||
package collector_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"gopkg.in/yaml.v1"
|
||||
)
|
||||
|
||||
var _ = Describe("Config Collector", func() {
|
||||
Describe("Options", func() {
|
||||
var options *Options
|
||||
|
||||
BeforeEach(func() {
|
||||
options = &Options{
|
||||
NoLogs: false,
|
||||
}
|
||||
})
|
||||
|
||||
It("applies a defined option function", func() {
|
||||
option := func(o *Options) error {
|
||||
o.NoLogs = true
|
||||
return nil
|
||||
}
|
||||
|
||||
Expect(options.NoLogs).To(BeFalse())
|
||||
Expect(options.Apply(option)).NotTo(HaveOccurred())
|
||||
Expect(options.NoLogs).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("MergeConfig", func() {
|
||||
var originalConfig, newConfig *Config
|
||||
BeforeEach(func() {
|
||||
originalConfig = &Config{}
|
||||
newConfig = &Config{}
|
||||
})
|
||||
|
||||
Context("different keys", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte(`#cloud-config
|
||||
name: Mario`), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte(`#cloud-config
|
||||
surname: Bros`), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("gets merged together", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
surname, isString := (*originalConfig)["surname"].(string)
|
||||
Expect(isString).To(BeTrue())
|
||||
Expect(surname).To(Equal("Bros"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("same keys", func() {
|
||||
Context("when the key is a map", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte(`#cloud-config
|
||||
info:
|
||||
name: Mario
|
||||
`), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte(`#cloud-config
|
||||
info:
|
||||
surname: Bros
|
||||
`), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
It("merges the keys", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
info, isMap := (*originalConfig)["info"].(Config)
|
||||
Expect(isMap).To(BeTrue())
|
||||
Expect(info["name"]).To(Equal("Mario"))
|
||||
Expect(info["surname"]).To(Equal("Bros"))
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
Expect(info).To(HaveLen(2))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the key is a string", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte("#cloud-config\nname: Mario"), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = yaml.Unmarshal([]byte("#cloud-config\nname: Luigi"), newConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("overwrites", func() {
|
||||
Expect(originalConfig.MergeConfig(newConfig)).ToNot(HaveOccurred())
|
||||
name, isString := (*originalConfig)["name"].(string)
|
||||
Expect(isString).To(BeTrue())
|
||||
Expect(name).To(Equal("Luigi"))
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("MergeConfigURL", func() {
|
||||
var originalConfig *Config
|
||||
BeforeEach(func() {
|
||||
originalConfig = &Config{}
|
||||
})
|
||||
|
||||
Context("when there is no config_url defined", func() {
|
||||
BeforeEach(func() {
|
||||
err := yaml.Unmarshal([]byte("#cloud-config\nname: Mario"), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("does nothing", func() {
|
||||
Expect(originalConfig.MergeConfigURL()).ToNot(HaveOccurred())
|
||||
Expect(*originalConfig).To(HaveLen(1))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there is a chain of config_url defined", func() {
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
var err error
|
||||
var tmpDir string
|
||||
var originalConfig *Config
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
closeFunc, port, err = startAssetServer(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
originalConfig = &Config{}
|
||||
err = yaml.Unmarshal([]byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/config1.yaml
|
||||
name: Mario
|
||||
surname: Bros
|
||||
info:
|
||||
job: plumber
|
||||
`, port)), originalConfig)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err := os.WriteFile(path.Join(tmpDir, "config1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/config2.yaml
|
||||
surname: Bras
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(tmpDir, "config2.yaml"), []byte(`#cloud-config
|
||||
|
||||
info:
|
||||
girlfriend: princess
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
closeFunc()
|
||||
err := os.RemoveAll(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("merges them all together", func() {
|
||||
err := originalConfig.MergeConfigURL()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
name, ok := (*originalConfig)["name"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(name).To(Equal("Mario"))
|
||||
|
||||
surname, ok := (*originalConfig)["surname"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(surname).To(Equal("Bras"))
|
||||
|
||||
info, ok := (*originalConfig)["info"].(Config)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(info["job"]).To(Equal("plumber"))
|
||||
Expect(info["girlfriend"]).To(Equal("princess"))
|
||||
|
||||
Expect(*originalConfig).To(HaveLen(4))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("deepMerge", func() {
|
||||
Context("different types", func() {
|
||||
a := map[string]interface{}{}
|
||||
b := []string{}
|
||||
|
||||
It("merges", func() {
|
||||
_, err := DeepMerge(a, b)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(Equal("cannot merge map[string]interface {} with []string"))
|
||||
|
||||
_, err = DeepMerge(b, a)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(Equal("cannot merge []string with map[string]interface {}"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("simple slices", func() {
|
||||
a := []interface{}{"one", "three"}
|
||||
b := []interface{}{"two", 4}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal([]interface{}{"one", "three", "two", 4}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("slices containing maps", func() {
|
||||
a := []interface{}{
|
||||
map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{
|
||||
"kairos": map[string]interface{}{
|
||||
"passwd": "kairos",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
b := []interface{}{
|
||||
map[string]interface{}{
|
||||
"users": []interface{}{
|
||||
map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"passwd": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
users := c.([]interface{})[0].(map[string]interface{})["users"]
|
||||
Expect(users).To(HaveLen(1))
|
||||
Expect(users).To(Equal([]interface{}{
|
||||
map[string]interface{}{
|
||||
"kairos": map[string]interface{}{
|
||||
"passwd": "kairos",
|
||||
},
|
||||
"foo": map[string]interface{}{
|
||||
"passwd": "bar",
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("empty map", func() {
|
||||
a := map[string]interface{}{}
|
||||
b := map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("simple map", func() {
|
||||
a := map[string]interface{}{
|
||||
"es": "uno",
|
||||
"nl": "een",
|
||||
"#": 0,
|
||||
}
|
||||
b := map[string]interface{}{
|
||||
"en": "one",
|
||||
"nl": "één",
|
||||
"de": "Eins",
|
||||
"#": 1,
|
||||
}
|
||||
|
||||
It("merges", func() {
|
||||
c, err := DeepMerge(a, b)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(c).To(Equal(map[string]interface{}{
|
||||
"#": 1,
|
||||
"de": "Eins",
|
||||
"en": "one",
|
||||
"es": "uno",
|
||||
"nl": "één",
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Scan", func() {
|
||||
Context("duplicated configs", func() {
|
||||
var cmdLinePath, tmpDir1 string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(`#cloud-config
|
||||
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
hostname: kairos-{{ trunc 4 .Random }}
|
||||
|
||||
install:
|
||||
auto: true
|
||||
reboot: true
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir1, "local_config_2.yaml"), []byte(`#cloud-config
|
||||
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
hostname: kairos-{{ trunc 4 .Random }}
|
||||
|
||||
install:
|
||||
auto: true
|
||||
reboot: true
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should be the same as just one of them", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
fmt.Println(c.String())
|
||||
Expect(c.String()).To(Equal(`#cloud-config
|
||||
|
||||
install:
|
||||
auto: true
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/kubo
|
||||
targets:
|
||||
- container://ttl.sh/97d4530c-df80-4eb4-9ae7-39f8f90c26e5:8h
|
||||
device: auto
|
||||
grub_options:
|
||||
extra_cmdline: foobarzz
|
||||
reboot: true
|
||||
stages:
|
||||
initramfs:
|
||||
- hostname: kairos-{{ trunc 4 .Random }}
|
||||
name: Set user and password
|
||||
users:
|
||||
kairos:
|
||||
passwd: kairos
|
||||
`))
|
||||
})
|
||||
})
|
||||
Context("Deep merge maps within arrays", func() {
|
||||
var cmdLinePath, tmpDir1 string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(`#cloud-config
|
||||
install:
|
||||
auto: true
|
||||
reboot: false
|
||||
poweroff: false
|
||||
grub_options:
|
||||
extra_cmdline: "console=tty0"
|
||||
options:
|
||||
device: /dev/sda
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
kairos:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: kairos
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir1, "local_config_2.yaml"), []byte(`#cloud-config
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
foo:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: bar
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("merges all the sources accordingly", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(c.String()).To(Equal(`#cloud-config
|
||||
|
||||
install:
|
||||
auto: true
|
||||
grub_options:
|
||||
extra_cmdline: console=tty0
|
||||
poweroff: false
|
||||
reboot: false
|
||||
options:
|
||||
device: /dev/sda
|
||||
stages:
|
||||
initramfs:
|
||||
- users:
|
||||
foo:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: bar
|
||||
kairos:
|
||||
groups:
|
||||
- sudo
|
||||
passwd: kairos
|
||||
`))
|
||||
})
|
||||
})
|
||||
|
||||
Context("multiple sources are defined", func() {
|
||||
var cmdLinePath, serverDir, tmpDir, tmpDir1, tmpDir2 string
|
||||
var err error
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare the cmdline config_url chain
|
||||
serverDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
closeFunc, port, err = startAssetServer(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cmdLinePath = createRemoteConfigs(serverDir, port)
|
||||
|
||||
tmpDir1, err = os.MkdirTemp("", "config1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err := os.WriteFile(path.Join(tmpDir1, "local_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_3.yaml
|
||||
local_key_1: local_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_3.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_4.yaml
|
||||
remote_key_3: remote_value_3
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_4.yaml"), []byte(`#cloud-config
|
||||
|
||||
options:
|
||||
remote_option_1: remote_option_value_1
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tmpDir2, err = os.MkdirTemp("", "config2")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(tmpDir2, "local_config_2.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_5.yaml
|
||||
local_key_2: local_value_2
|
||||
`, port)), os.ModePerm)
|
||||
err = os.WriteFile(path.Join(tmpDir2, "local_config_3.yaml"), []byte(`#cloud-config
|
||||
local_key_3: local_value_3
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_5.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_6.yaml
|
||||
remote_key_4: remote_value_4
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_6.yaml"), []byte(`#cloud-config
|
||||
|
||||
options:
|
||||
remote_option_2: remote_option_value_2
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
err = os.RemoveAll(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir1)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir2)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
closeFunc()
|
||||
})
|
||||
|
||||
It("merges all the sources accordingly", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(
|
||||
MergeBootLine,
|
||||
WithBootCMDLineFile(cmdLinePath),
|
||||
Directories(tmpDir1, tmpDir2),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
configURL, ok := (*c)["config_url"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(configURL).To(MatchRegexp("remote_config_2.yaml"))
|
||||
|
||||
k := (*c)["local_key_1"].(string)
|
||||
Expect(k).To(Equal("local_value_1"))
|
||||
k = (*c)["local_key_2"].(string)
|
||||
Expect(k).To(Equal("local_value_2"))
|
||||
k = (*c)["local_key_3"].(string)
|
||||
Expect(k).To(Equal("local_value_3"))
|
||||
k = (*c)["remote_key_1"].(string)
|
||||
Expect(k).To(Equal("remote_value_1"))
|
||||
k = (*c)["remote_key_2"].(string)
|
||||
Expect(k).To(Equal("remote_value_2"))
|
||||
k = (*c)["remote_key_3"].(string)
|
||||
Expect(k).To(Equal("remote_value_3"))
|
||||
k = (*c)["remote_key_4"].(string)
|
||||
Expect(k).To(Equal("remote_value_4"))
|
||||
|
||||
options := (*c)["options"].(Config)
|
||||
Expect(options["foo"]).To(Equal("bar"))
|
||||
Expect(options["remote_option_1"]).To(Equal("remote_option_value_1"))
|
||||
Expect(options["remote_option_2"]).To(Equal("remote_option_value_2"))
|
||||
|
||||
player := (*c)["player"].(Config)
|
||||
Expect(player["name"]).NotTo(Equal("Toad"))
|
||||
Expect(player["surname"]).To(Equal("Bros"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when files have invalid or missing headers", func() {
|
||||
var serverDir, tmpDir string
|
||||
var err error
|
||||
var closeFunc ServerCloseFunc
|
||||
var port int
|
||||
|
||||
BeforeEach(func() {
|
||||
// Prepare the cmdline config_url chain
|
||||
serverDir, err = os.MkdirTemp("", "config_url_chain")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
closeFunc, port, err = startAssetServer(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tmpDir, err = os.MkdirTemp("", "config")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Local configs
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/remote_config_1.yaml
|
||||
local_key_1: local_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// missing header
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config_2.yaml"),
|
||||
[]byte("local_key_2: local_value_2"), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Remote config with valid header
|
||||
err := os.WriteFile(path.Join(serverDir, "remote_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
config_url: http://127.0.0.1:%d/remote_config_2.yaml
|
||||
remote_key_1: remote_value_1`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Remote config with invalid header
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_2.yaml"), []byte(`#invalid-header
|
||||
remote_key_2: remote_value_2`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
closeFunc()
|
||||
err = os.RemoveAll(serverDir)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.RemoveAll(tmpDir)
|
||||
})
|
||||
|
||||
It("ignores them", func() {
|
||||
o := &Options{}
|
||||
err := o.Apply(Directories(tmpDir), NoLogs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect((*c)["local_key_2"]).To(BeNil())
|
||||
Expect((*c)["remote_key_2"]).To(BeNil())
|
||||
|
||||
// sanity check, the rest should be there
|
||||
v, ok := (*c)["config_url"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(MatchRegexp("remote_config_2.yaml"))
|
||||
|
||||
v, ok = (*c)["local_key_1"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(Equal("local_value_1"))
|
||||
|
||||
v, ok = (*c)["remote_key_1"].(string)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(v).To(Equal("remote_value_1"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("String", func() {
|
||||
var conf *Config
|
||||
BeforeEach(func() {
|
||||
conf = &Config{}
|
||||
err := yaml.Unmarshal([]byte("name: Mario"), conf)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns the YAML string representation of the Config", func() {
|
||||
s, err := conf.String()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(s).To(Equal(`#cloud-config
|
||||
|
||||
name: Mario
|
||||
`), s)
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Query", func() {
|
||||
var tmpDir string
|
||||
var err error
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpDir, err = os.MkdirTemp("", "config")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(filepath.Join(tmpDir, "b"), []byte(`zz.foo="baa" options.foo=bar`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(path.Join(tmpDir, "local_config.yaml"), []byte(`#cloud-config
|
||||
local_key_1: local_value_1
|
||||
some:
|
||||
other:
|
||||
key: 3
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("can query for keys", func() {
|
||||
o := &Options{}
|
||||
|
||||
err = o.Apply(MergeBootLine, Directories(tmpDir),
|
||||
WithBootCMDLineFile(filepath.Join(tmpDir, "b")),
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
c, err := Scan(o, config.FilterKeys)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
v, err := c.Query("local_key_1")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(v).To(Equal("local_value_1\n"))
|
||||
v, err = c.Query("some")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(v).To(Equal("other:\n key: 3\n"))
|
||||
v, err = c.Query("some.other")
|
||||
Expect(v).To(Equal("key: 3\n"))
|
||||
v, err = c.Query("some.other.key")
|
||||
Expect(v).To(Equal("3\n"))
|
||||
Expect(c.Query("options")).To(Equal("foo: bar\n"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func createRemoteConfigs(serverDir string, port int) string {
|
||||
err := os.WriteFile(path.Join(serverDir, "remote_config_1.yaml"), []byte(fmt.Sprintf(`#cloud-config
|
||||
|
||||
config_url: http://127.0.0.1:%d/remote_config_2.yaml
|
||||
player:
|
||||
remote_key_1: remote_value_1
|
||||
`, port)), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = os.WriteFile(path.Join(serverDir, "remote_config_2.yaml"), []byte(`#cloud-config
|
||||
|
||||
player:
|
||||
surname: Bros
|
||||
remote_key_2: remote_value_2
|
||||
`), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
cmdLinePath := filepath.Join(serverDir, "cmdline")
|
||||
// We put the cmdline in the same dir, it doesn't matter.
|
||||
cmdLine := fmt.Sprintf(`config_url="http://127.0.0.1:%d/remote_config_1.yaml" player.name="Toad" options.foo=bar`, port)
|
||||
err = os.WriteFile(cmdLinePath, []byte(cmdLine), os.ModePerm)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
return cmdLinePath
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package collector
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Options struct {
|
||||
ScanDir []string
|
||||
BootCMDLineFile string
|
||||
MergeBootCMDLine bool
|
||||
NoLogs bool
|
||||
StrictValidation bool
|
||||
}
|
||||
|
||||
type Option func(o *Options) error
|
||||
|
||||
var NoLogs Option = func(o *Options) error {
|
||||
o.NoLogs = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// SoftErr prints a warning if err is no nil and NoLogs is not true.
|
||||
// It's use to wrap the same handling happening in multiple places.
|
||||
//
|
||||
// TODO: Switch to a standard logging library (e.g. verbose, silent mode etc).
|
||||
func (o *Options) SoftErr(message string, err error) {
|
||||
if !o.NoLogs && err != nil {
|
||||
fmt.Printf("WARNING: %s, %s\n", message, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) Apply(opts ...Option) error {
|
||||
for _, oo := range opts {
|
||||
if err := oo(o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var MergeBootLine = func(o *Options) error {
|
||||
o.MergeBootCMDLine = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func WithBootCMDLineFile(s string) Option {
|
||||
return func(o *Options) error {
|
||||
o.BootCMDLineFile = s
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func StrictValidation(v bool) Option {
|
||||
return func(o *Options) error {
|
||||
o.StrictValidation = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Directories(d ...string) Option {
|
||||
return func(o *Options) error {
|
||||
o.ScanDir = d
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package collector_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Config Collector Suite")
|
||||
}
|
||||
|
||||
type ServerCloseFunc func()
|
||||
|
||||
func startAssetServer(path string) (ServerCloseFunc, int, error) {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
port := listener.Addr().(*net.TCPAddr).Port
|
||||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
err := http.Serve(listener, http.FileServer(http.Dir(path)))
|
||||
select {
|
||||
case <-ctx.Done(): // We closed it with the CancelFunc, ignore the error
|
||||
return
|
||||
default: // We didnt' close it, return the error
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}()
|
||||
|
||||
stopFunc := func() {
|
||||
cancelFunc()
|
||||
listener.Close()
|
||||
}
|
||||
|
||||
return stopFunc, port, nil
|
||||
}
|
@ -1,207 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/bundles"
|
||||
"github.com/kairos-io/kairos/v2/pkg/config/collector"
|
||||
schema "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
yip "github.com/mudler/yip/pkg/schema"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultWebUIListenAddress = ":8080"
|
||||
FilePrefix = "file://"
|
||||
)
|
||||
|
||||
type Install struct {
|
||||
Auto bool `yaml:"auto,omitempty"`
|
||||
Reboot bool `yaml:"reboot,omitempty"`
|
||||
Device string `yaml:"device,omitempty"`
|
||||
Poweroff bool `yaml:"poweroff,omitempty"`
|
||||
GrubOptions map[string]string `yaml:"grub_options,omitempty"`
|
||||
Bundles Bundles `yaml:"bundles,omitempty"`
|
||||
Encrypt []string `yaml:"encrypted_partitions,omitempty"`
|
||||
SkipEncryptCopyPlugins bool `yaml:"skip_copy_kcrypt_plugin,omitempty"`
|
||||
Env []string `yaml:"env,omitempty"`
|
||||
Image string `yaml:"image,omitempty"`
|
||||
EphemeralMounts []string `yaml:"ephemeral_mounts,omitempty"`
|
||||
BindMounts []string `yaml:"bind_mounts,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Install *Install `yaml:"install,omitempty"`
|
||||
collector.Config `yaml:"-"`
|
||||
// TODO: Remove this too?
|
||||
ConfigURL string `yaml:"config_url,omitempty"`
|
||||
Options map[string]string `yaml:"options,omitempty"`
|
||||
FailOnBundleErrors bool `yaml:"fail_on_bundles_errors,omitempty"`
|
||||
Bundles Bundles `yaml:"bundles,omitempty"`
|
||||
GrubOptions map[string]string `yaml:"grub_options,omitempty"`
|
||||
Env []string `yaml:"env,omitempty"`
|
||||
}
|
||||
|
||||
type Bundles []Bundle
|
||||
|
||||
type Bundle struct {
|
||||
Repository string `yaml:"repository,omitempty"`
|
||||
Rootfs string `yaml:"rootfs_path,omitempty"`
|
||||
DB string `yaml:"db_path,omitempty"`
|
||||
LocalFile bool `yaml:"local_file,omitempty"`
|
||||
Targets []string `yaml:"targets,omitempty"`
|
||||
}
|
||||
|
||||
const DefaultHeader = "#cloud-config"
|
||||
|
||||
func HasHeader(userdata, head string) (bool, string) {
|
||||
header := strings.SplitN(userdata, "\n", 2)[0]
|
||||
|
||||
// Trim trailing whitespaces
|
||||
header = strings.TrimRightFunc(header, unicode.IsSpace)
|
||||
|
||||
if head != "" {
|
||||
return head == header, header
|
||||
}
|
||||
return (header == DefaultHeader) || (header == "#kairos-config") || (header == "#node-config"), header
|
||||
}
|
||||
|
||||
func (b Bundles) Options() (res [][]bundles.BundleOption) {
|
||||
for _, bundle := range b {
|
||||
for _, t := range bundle.Targets {
|
||||
opts := []bundles.BundleOption{bundles.WithRepository(bundle.Repository), bundles.WithTarget(t)}
|
||||
if bundle.Rootfs != "" {
|
||||
opts = append(opts, bundles.WithRootFS(bundle.Rootfs))
|
||||
}
|
||||
if bundle.DB != "" {
|
||||
opts = append(opts, bundles.WithDBPath(bundle.DB))
|
||||
}
|
||||
if bundle.LocalFile {
|
||||
opts = append(opts, bundles.WithLocalFile(true))
|
||||
}
|
||||
res = append(res, opts)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HasConfigURL returns true if ConfigURL has been set and false if it's empty.
|
||||
func (c Config) HasConfigURL() bool {
|
||||
return c.ConfigURL != ""
|
||||
}
|
||||
|
||||
// FilterKeys is used to pass to any other pkg which might want to see which part of the config matches the Kairos config.
|
||||
func FilterKeys(d []byte) ([]byte, error) {
|
||||
cmdLineFilter := Config{}
|
||||
err := yaml.Unmarshal(d, &cmdLineFilter)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
out, err := yaml.Marshal(cmdLineFilter)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func Scan(opts ...collector.Option) (c *Config, err error) {
|
||||
result := &Config{}
|
||||
|
||||
o := &collector.Options{}
|
||||
if err := o.Apply(opts...); err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
genericConfig, err := collector.Scan(o, FilterKeys)
|
||||
if err != nil {
|
||||
return result, err
|
||||
|
||||
}
|
||||
result.Config = *genericConfig
|
||||
configStr, err := genericConfig.String()
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal([]byte(configStr), result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
kc, err := schema.NewConfigFromYAML(configStr, schema.RootSchema{})
|
||||
if err != nil {
|
||||
if !o.NoLogs && !o.StrictValidation {
|
||||
fmt.Printf("WARNING: %s\n", err.Error())
|
||||
}
|
||||
|
||||
if o.StrictValidation {
|
||||
return result, fmt.Errorf("ERROR: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if !kc.IsValid() {
|
||||
if !o.NoLogs && !o.StrictValidation {
|
||||
fmt.Printf("WARNING: %s\n", kc.ValidationError.Error())
|
||||
}
|
||||
|
||||
if o.StrictValidation {
|
||||
return result, fmt.Errorf("ERROR: %s", kc.ValidationError.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type Stage string
|
||||
|
||||
const (
|
||||
NetworkStage Stage = "network"
|
||||
)
|
||||
|
||||
func (n Stage) String() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
func SaveCloudConfig(name Stage, yc yip.YipConfig) error {
|
||||
dnsYAML, err := yaml.Marshal(yc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(filepath.Join("usr", "local", "cloud-config", fmt.Sprintf("100_%s.yaml", name)), dnsYAML, 0700)
|
||||
}
|
||||
|
||||
func FromString(s string, o interface{}) error {
|
||||
return yaml.Unmarshal([]byte(s), o)
|
||||
}
|
||||
|
||||
func MergeYAML(objs ...interface{}) ([]byte, error) {
|
||||
content := [][]byte{}
|
||||
for _, o := range objs {
|
||||
dat, err := yaml.Marshal(o)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
content = append(content, dat)
|
||||
}
|
||||
|
||||
finalData := make(map[string]interface{})
|
||||
|
||||
for _, c := range content {
|
||||
if err := yaml.Unmarshal(c, &finalData); err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return yaml.Marshal(finalData)
|
||||
}
|
||||
|
||||
func AddHeader(header, data string) string {
|
||||
return fmt.Sprintf("%s\n%s", header, data)
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Config Suite")
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// Copyright © 2022 Ettore Di Giacinto <mudler@c3os.io>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
// . "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
// . "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type TConfig struct {
|
||||
Kairos struct {
|
||||
OtherKey string `yaml:"other_key"`
|
||||
NetworkToken string `yaml:"network_token"`
|
||||
} `yaml:"kairos"`
|
||||
}
|
||||
|
||||
var _ = Describe("Config", func() {
|
||||
var d string
|
||||
BeforeEach(func() {
|
||||
d, _ = os.MkdirTemp("", "xxxx")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
if d != "" {
|
||||
os.RemoveAll(d)
|
||||
}
|
||||
})
|
||||
})
|
@ -1,79 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
jsonschemago "github.com/swaggest/jsonschema-go"
|
||||
)
|
||||
|
||||
// InstallSchema represents the install block in the Kairos configuration. It is used to drive automatic installations without user interaction.
|
||||
type InstallSchema struct {
|
||||
_ struct{} `title:"Kairos Schema: Install block" description:"The install block is to drive automatic installations without user interaction."`
|
||||
Auto bool `json:"auto,omitempty" description:"Set to true when installing without Pairing"`
|
||||
BindMounts []string `json:"bind_mounts,omitempty"`
|
||||
Bundles []BundleSchema `json:"bundles,omitempty" description:"Add bundles in runtime"`
|
||||
Device string `json:"device,omitempty" pattern:"^(auto|/|(/[a-zA-Z0-9_-]+)+)$" description:"Device for automated installs" examples:"[\"auto\",\"/dev/sda\"]"`
|
||||
EphemeralMounts []string `json:"ephemeral_mounts,omitempty"`
|
||||
EncryptedPartitions []string `json:"encrypted_partitions,omitempty"`
|
||||
Env []interface{} `json:"env,omitempty"`
|
||||
GrubOptionsSchema `json:"grub_options,omitempty"`
|
||||
Image string `json:"image,omitempty" description:"Use a different container image for the installation"`
|
||||
PowerManagement
|
||||
SkipEncryptCopyPlugins bool `json:"skip_copy_kcrypt_plugin,omitempty"`
|
||||
}
|
||||
|
||||
// BundleSchema represents the bundle block which can be used in different places of the Kairos configuration. It is used to reference a bundle and its confguration.
|
||||
type BundleSchema struct {
|
||||
DB string `json:"db_path,omitempty"`
|
||||
LocalFile bool `json:"local_file,omitempty"`
|
||||
Repository string `json:"repository,omitempty"`
|
||||
Rootfs string `json:"rootfs_path,omitempty"`
|
||||
Targets []string `json:"targets,omitempty"`
|
||||
}
|
||||
|
||||
// GrubOptionsSchema represents the grub options block which can be used in different places of the Kairos configuration. It is used to configure grub.
|
||||
type GrubOptionsSchema struct {
|
||||
DefaultFallback string `json:"default_fallback,omitempty" description:"Sets default fallback logic"`
|
||||
DefaultMenuEntry string `json:"default_menu_entry,omitempty" description:"Change GRUB menu entry"`
|
||||
ExtraActiveCmdline string `json:"extra_active_cmdline,omitempty" description:"Additional Kernel option cmdline to apply just for active"`
|
||||
ExtraCmdline string `json:"extra_cmdline,omitempty" description:"Additional Kernel option cmdline to apply"`
|
||||
ExtraPassiveCmdline string `json:"extra_passive_cmdline,omitempty" description:"Additional Kernel option cmdline to apply just for passive"`
|
||||
ExtraRecoveryCmdline string `json:"extra_recovery_cmdline,omitempty" description:"Set additional boot commands when booting into recovery"`
|
||||
NextEntry string `json:"next_entry,omitempty" description:"Set the next reboot entry."`
|
||||
SavedEntry string `json:"saved_entry,omitempty" description:"Set the default boot entry."`
|
||||
}
|
||||
|
||||
// PowerManagement is a meta structure to hold the different rules for managing power, which are not compatible between each other.
|
||||
type PowerManagement struct {
|
||||
}
|
||||
|
||||
// NoPowerManagement is a meta structure used when the user does not define any power management options or when the user does not want to reboot or poweroff the machine.
|
||||
type NoPowerManagement struct {
|
||||
Reboot bool `json:"reboot,omitempty" const:"false" default:"false" description:"Reboot after installation"`
|
||||
Poweroff bool `json:"poweroff,omitempty" const:"false" default:"false" description:"Power off after installation"`
|
||||
}
|
||||
|
||||
// RebootOnly is a meta structure used to enforce that when the reboot option is set, the poweroff option is not set.
|
||||
type RebootOnly struct {
|
||||
Reboot bool `json:"reboot,omitempty" const:"true" default:"false" required:"true" description:"Reboot after installation"`
|
||||
Poweroff bool `json:"poweroff,omitempty" const:"false" default:"false" description:"Power off after installation"`
|
||||
}
|
||||
|
||||
// PowerOffOnly is a meta structure used to enforce that when the poweroff option is set, the reboot option is not set.
|
||||
type PowerOffOnly struct {
|
||||
Reboot bool `json:"reboot,omitempty" const:"false" default:"false" description:"Reboot after installation"`
|
||||
Poweroff bool `json:"poweroff,omitempty" const:"true" default:"false" required:"true" description:"Power off after installation"`
|
||||
}
|
||||
|
||||
var _ jsonschemago.OneOfExposer = PowerManagement{}
|
||||
|
||||
// The OneOfModel interface is only needed for the tests that check the new schemas contain all needed fields
|
||||
// it can be removed once the new schema is the single source of truth.
|
||||
type OneOfModel interface {
|
||||
JSONSchemaOneOf() []interface{}
|
||||
}
|
||||
|
||||
// JSONSchemaOneOf defines that different which are the different valid power management rules and states that one and only one of them needs to be validated for the entire schema to be valid.
|
||||
func (PowerManagement) JSONSchemaOneOf() []interface{} {
|
||||
return []interface{}{
|
||||
NoPowerManagement{}, RebootOnly{}, PowerOffOnly{},
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Install Schema", func() {
|
||||
var config *KConfig
|
||||
var err error
|
||||
var yaml string
|
||||
|
||||
JustBeforeEach(func() {
|
||||
config, err = NewConfigFromYAML(yaml, InstallSchema{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when device is auto", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: auto`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when device is a path", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: /dev/sda`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when device is other than a path or auto", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: foobar`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(
|
||||
strings.Contains(config.ValidationError.Error(),
|
||||
"does not match pattern '^(auto|/|(/[a-zA-Z0-9_-]+)+)$'",
|
||||
),
|
||||
).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when reboot and poweroff are true", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: /dev/sda
|
||||
reboot: true
|
||||
poweroff: true`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp("value must be false"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when reboot is true and poweroff is false", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: /dev/sda
|
||||
reboot: true
|
||||
poweroff: false`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when reboot is false and poweroff is true", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: /dev/sda
|
||||
reboot: false
|
||||
poweroff: true`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with no power management set", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: /dev/sda`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with all possible options", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
device: "/dev/sda"
|
||||
reboot: true
|
||||
auto: true
|
||||
image: "docker:.."
|
||||
bundles:
|
||||
- rootfs_path: /usr/local/lib/extensions/<name>
|
||||
targets:
|
||||
- container://<image>
|
||||
grub_options:
|
||||
extra_cmdline: "config_url=http://"
|
||||
extra_active_cmdline: "config_url=http://"
|
||||
extra_passive_cmdline: "config_url=http://"
|
||||
default_menu_entry: "foobar"
|
||||
env:
|
||||
- foo=barevice: /dev/sda`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
@ -1,68 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
jsonschemago "github.com/swaggest/jsonschema-go"
|
||||
)
|
||||
|
||||
// P2PSchema represents the P2P block in the Kairos configuration. It is used to enables and configure the p2p full-mesh functionalities.
|
||||
type P2PSchema struct {
|
||||
_ struct{} `title:"Kairos Schema: P2P block" description:"The p2p block enables the p2p full-mesh functionalities."`
|
||||
Role string `json:"role,omitempty" default:"none" enum:"[\"master\",\"worker\",\"none\"]"`
|
||||
NetworkID string `json:"network_id,omitempty" description:"User defined network-id. Can be used to have multiple clusters in the same network"`
|
||||
DNS bool `json:"dns,omitempty" description:"Enable embedded DNS See also: https://mudler.github.io/edgevpn/docs/concepts/overview/dns/"`
|
||||
DisableDHT bool `json:"disable_dht,omitempty" default:"true" description:"Disabling DHT makes co-ordination to discover nodes only in the local network"`
|
||||
P2PNetworkExtended
|
||||
VPN `json:"vpn,omitempty"`
|
||||
}
|
||||
|
||||
// KubeVIPSchema represents the kubevip block in the Kairos configuration. It sets the Elastic IP used in KubeVIP. Only valid with p2p.
|
||||
type KubeVIPSchema struct {
|
||||
_ struct{} `title:"Kairos Schema: KubeVIP block" description:"Sets the Elastic IP used in KubeVIP. Only valid with p2p"`
|
||||
EIP string `json:"eip,omitempty" example:"192.168.1.110"`
|
||||
ManifestURL string `json:"manifest_url,omitempty" description:"Specify a manifest URL for KubeVIP." default:""`
|
||||
Enable bool `json:"enable,omitempty" description:"Enables KubeVIP"`
|
||||
Interface bool `json:"interface,omitempty" description:"Specifies a KubeVIP Interface" example:"ens18"`
|
||||
}
|
||||
|
||||
// P2PNetworkExtended is a meta structure to hold the different rules for managing the P2P network, which are not compatible between each other.
|
||||
type P2PNetworkExtended struct {
|
||||
}
|
||||
|
||||
// P2PAutoDisabled is used to validate that when p2p.auto is disabled, then neither p2p.auto.ha not p2p.network_token can be set.
|
||||
type P2PAutoDisabled struct {
|
||||
NetworkToken string `json:"network_token,omitempty" const:"" required:"true"`
|
||||
Auto struct {
|
||||
Enable bool `json:"enable" const:"false" required:"true"`
|
||||
Ha struct {
|
||||
Enable bool `json:"enable" const:"false"`
|
||||
} `json:"ha"`
|
||||
} `json:"auto"`
|
||||
}
|
||||
|
||||
// P2PAutoEnabled is used to validate that when p2p.auto is set, p2p.network_token has to be set.
|
||||
type P2PAutoEnabled struct {
|
||||
NetworkToken string `json:"network_token" required:"true" minLength:"1" description:"network_token is the shared secret used by the nodes to co-ordinate with p2p"`
|
||||
Auto struct {
|
||||
Enable bool `json:"enable,omitempty" const:"true"`
|
||||
Ha struct {
|
||||
Enable bool `json:"enable" const:"true"`
|
||||
MasterNodes int `json:"master_nodes,omitempty" minimum:"1" description:"Number of HA additional master nodes. A master node is always required for creating the cluster and is implied."`
|
||||
} `json:"ha"`
|
||||
} `json:"auto,omitempty"`
|
||||
}
|
||||
|
||||
var _ jsonschemago.OneOfExposer = P2PNetworkExtended{}
|
||||
|
||||
// JSONSchemaOneOf defines that different which are the different valid p2p network rules and states that one and only one of them needs to be validated for the entire schema to be valid.
|
||||
func (P2PNetworkExtended) JSONSchemaOneOf() []interface{} {
|
||||
return []interface{}{
|
||||
P2PAutoEnabled{}, P2PAutoDisabled{},
|
||||
}
|
||||
}
|
||||
|
||||
// VPN represents the vpn block in the Kairos configuration.
|
||||
type VPN struct {
|
||||
Create bool `json:"vpn,omitempty" default:"true"`
|
||||
Use bool `json:"use,omitempty" default:"true"`
|
||||
Envs []interface{} `json:"env,omitempty"`
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("P2P Schema", func() {
|
||||
var config *KConfig
|
||||
var err error
|
||||
var yaml string
|
||||
|
||||
JustBeforeEach(func() {
|
||||
config, err = NewConfigFromYAML(yaml, P2PSchema{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("with role master", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
role: master
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with role worker", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
role: worker
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with role none", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
role: none
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("with other role", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
role: foobar
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp(`value must be one of "master", "worker", "none"`))
|
||||
})
|
||||
})
|
||||
|
||||
Context("With a network_token and p2p.auto.enable = false", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="
|
||||
auto:
|
||||
enable: false`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(
|
||||
strings.Contains(config.ValidationError.Error(), `value must be true`),
|
||||
).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With an empty network_token and p2p.auto.enable = true", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: ""
|
||||
auto:
|
||||
enable: true`
|
||||
})
|
||||
|
||||
It("Fails", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(
|
||||
strings.Contains(config.ValidationError.Error(),
|
||||
"length must be >= 1, but got 0",
|
||||
),
|
||||
).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With a network_token and p2p.auto.enable = true", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="
|
||||
auto:
|
||||
enable: true`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With a p2p.auto.enable = false and ha.enable = true", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: ""
|
||||
auto:
|
||||
enable: false
|
||||
ha:
|
||||
enable: true`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp("(length must be >= 1, but got 0|value must be true)"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("HA with 0 master nodes", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="
|
||||
auto:
|
||||
enable: true
|
||||
ha:
|
||||
enable: true
|
||||
master_nodes: 0`
|
||||
})
|
||||
|
||||
It("fails", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp("must be >= 1 but found 0"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("HA", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="
|
||||
auto:
|
||||
enable: true
|
||||
ha:
|
||||
enable: true
|
||||
master_nodes: 2`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("kubevip", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
network_token: "b3RwOgogIGRoYWdlX3NpemU6IDIwOTcxNTIwCg=="
|
||||
auto:
|
||||
enable: true
|
||||
ha:
|
||||
enable: true
|
||||
master_nodes: 2`
|
||||
})
|
||||
|
||||
It("succeedes", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
@ -1,106 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/santhosh-tekuri/jsonschema/v5"
|
||||
jsonschemago "github.com/swaggest/jsonschema-go"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// RootSchema groups all the different schemas of the Kairos configuration together.
|
||||
type RootSchema struct {
|
||||
_ struct{} `title:"Kairos Schema" description:"Defines all valid Kairos configuration attributes."`
|
||||
Bundles []BundleSchema `json:"bundles,omitempty" description:"Add bundles in runtime"`
|
||||
ConfigURL string `json:"config_url,omitempty" description:"URL download configuration from."`
|
||||
Env []string `json:"env,omitempty"`
|
||||
FailOnBundleErrors bool `json:"fail_on_bundles_errors,omitempty"`
|
||||
GrubOptionsSchema `json:"grub_options,omitempty"`
|
||||
Install InstallSchema `json:"install,omitempty"`
|
||||
Options []interface{} `json:"options,omitempty" description:"Various options."`
|
||||
Users []UserSchema `json:"users,omitempty" minItems:"1" required:"true"`
|
||||
P2P P2PSchema `json:"p2p,omitempty"`
|
||||
}
|
||||
|
||||
// KConfig is used to parse and validate Kairos configuration files.
|
||||
type KConfig struct {
|
||||
Source string
|
||||
parsed interface{}
|
||||
ValidationError error
|
||||
schemaType interface{}
|
||||
}
|
||||
|
||||
// GenerateSchema takes the given schema type and builds a JSON Schema out of it
|
||||
// if a URL is passed it will also add it as the $schema key, which is useful when
|
||||
// defining a version of a Root Schema which will be available online.
|
||||
func GenerateSchema(schemaType interface{}, url string) (string, error) {
|
||||
reflector := jsonschemago.Reflector{}
|
||||
|
||||
generatedSchema, err := reflector.Reflect(schemaType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if url != "" {
|
||||
generatedSchema.WithSchema(url)
|
||||
}
|
||||
|
||||
generatedSchemaJSON, err := json.MarshalIndent(generatedSchema, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(generatedSchemaJSON), nil
|
||||
}
|
||||
|
||||
func (kc *KConfig) validate() {
|
||||
generatedSchemaJSON, err := GenerateSchema(kc.schemaType, "")
|
||||
if err != nil {
|
||||
kc.ValidationError = err
|
||||
return
|
||||
}
|
||||
|
||||
sch, err := jsonschema.CompileString("schema.json", string(generatedSchemaJSON))
|
||||
if err != nil {
|
||||
kc.ValidationError = err
|
||||
return
|
||||
}
|
||||
|
||||
if err = sch.Validate(kc.parsed); err != nil {
|
||||
kc.ValidationError = err
|
||||
}
|
||||
}
|
||||
|
||||
// IsValid returns true if the schema rules of the configuration are valid.
|
||||
func (kc *KConfig) IsValid() bool {
|
||||
kc.validate()
|
||||
|
||||
return kc.ValidationError == nil
|
||||
}
|
||||
|
||||
// HasHeader returns true if the config has one of the valid headers.
|
||||
func (kc *KConfig) HasHeader() bool {
|
||||
var found bool
|
||||
|
||||
availableHeaders := []string{"#cloud-config", "#kairos-config", "#node-config"}
|
||||
for _, header := range availableHeaders {
|
||||
if strings.HasPrefix(kc.Source, header) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
// NewConfigFromYAML is a constructor for KConfig instances. The source of the configuration is passed in YAML and if there are any issues unmarshaling it will return an error.
|
||||
func NewConfigFromYAML(s string, st interface{}) (*KConfig, error) {
|
||||
kc := &KConfig{
|
||||
Source: s,
|
||||
schemaType: st,
|
||||
}
|
||||
|
||||
err := yaml.Unmarshal([]byte(s), &kc.parsed)
|
||||
if err != nil {
|
||||
return kc, err
|
||||
}
|
||||
return kc, nil
|
||||
}
|
@ -1,190 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config"
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func getTagName(s string) string {
|
||||
if len(s) < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if s == "-" {
|
||||
return ""
|
||||
}
|
||||
|
||||
f := func(c rune) bool {
|
||||
return c == '"' || c == ','
|
||||
}
|
||||
return s[:strings.IndexFunc(s, f)]
|
||||
}
|
||||
|
||||
func structContainsField(f, t string, str interface{}) bool {
|
||||
values := reflect.ValueOf(str)
|
||||
types := values.Type()
|
||||
|
||||
for j := 0; j < values.NumField(); j++ {
|
||||
tagName := getTagName(types.Field(j).Tag.Get("json"))
|
||||
if types.Field(j).Name == f || tagName == t {
|
||||
return true
|
||||
} else {
|
||||
if types.Field(j).Type.Kind() == reflect.Struct {
|
||||
if types.Field(j).Type.Name() != "" {
|
||||
model := reflect.New(types.Field(j).Type)
|
||||
if instance, ok := model.Interface().(OneOfModel); ok {
|
||||
for _, childSchema := range instance.JSONSchemaOneOf() {
|
||||
if structContainsField(f, t, childSchema) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func structFieldsContainedInOtherStruct(left, right interface{}) {
|
||||
leftValues := reflect.ValueOf(left)
|
||||
leftTypes := leftValues.Type()
|
||||
|
||||
for i := 0; i < leftValues.NumField(); i++ {
|
||||
leftTagName := getTagName(leftTypes.Field(i).Tag.Get("yaml"))
|
||||
leftFieldName := leftTypes.Field(i).Name
|
||||
if leftTypes.Field(i).IsExported() {
|
||||
It(fmt.Sprintf("Checks that the new schema contians the field %s", leftFieldName), func() {
|
||||
Expect(
|
||||
structContainsField(leftFieldName, leftTagName, right),
|
||||
).To(BeTrue())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("Schema", func() {
|
||||
Context("NewConfigFromYAML", func() {
|
||||
var config *KConfig
|
||||
var err error
|
||||
var yaml string
|
||||
|
||||
JustBeforeEach(func() {
|
||||
config, err = NewConfigFromYAML(yaml, RootSchema{})
|
||||
})
|
||||
|
||||
Context("While the new Schema is not the single source of truth", func() {
|
||||
structFieldsContainedInOtherStruct(Config{}, RootSchema{})
|
||||
})
|
||||
Context("While the new InstallSchema is not the single source of truth", func() {
|
||||
structFieldsContainedInOtherStruct(Install{}, InstallSchema{})
|
||||
})
|
||||
Context("While the new BundleSchema is not the single source of truth", func() {
|
||||
structFieldsContainedInOtherStruct(Bundle{}, BundleSchema{})
|
||||
})
|
||||
|
||||
Context("With invalid YAML syntax", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
this is:
|
||||
- invalid
|
||||
yaml`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(err.Error()).To(MatchRegexp("yaml: line 4: could not find expected ':'"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When `users` is empty", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
users: []`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp("minimum 1 items required, but found 0 items"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("without a valid header", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `---
|
||||
users:
|
||||
- name: kairos
|
||||
passwd: kairos`
|
||||
})
|
||||
|
||||
It("is successful but HasHeader returns false", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.HasHeader()).To(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With a valid config", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
users:
|
||||
- name: kairos
|
||||
passwd: kairos`
|
||||
})
|
||||
|
||||
It("is successful", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.HasHeader()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("GenerateSchema", func() {
|
||||
var url string
|
||||
var schema string
|
||||
var err error
|
||||
|
||||
type TestSchema struct {
|
||||
Key interface{} `json:"key,omitemtpy" required:"true"`
|
||||
}
|
||||
|
||||
JustBeforeEach(func() {
|
||||
schema, err = GenerateSchema(TestSchema{}, url)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("does not include the $schema key by default", func() {
|
||||
Expect(strings.Contains(schema, `$schema`)).To(BeFalse())
|
||||
})
|
||||
|
||||
It("can use any type of schma", func() {
|
||||
wants := `{
|
||||
"required": [
|
||||
"key"
|
||||
],
|
||||
"properties": {
|
||||
"key": {}
|
||||
},
|
||||
"type": "object"
|
||||
}`
|
||||
Expect(schema).To(Equal(wants))
|
||||
})
|
||||
|
||||
Context("with a URL", func() {
|
||||
BeforeEach(func() {
|
||||
url = "http://foobar"
|
||||
})
|
||||
|
||||
It("appends the $schema key", func() {
|
||||
Expect(strings.Contains(schema, `$schema": "http://foobar"`)).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
@ -1,13 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Config Schemas Suite")
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package config
|
||||
|
||||
// UserSchema represents the users block in the Kairos configuration. It allows the creation of users in the system.
|
||||
type UserSchema struct {
|
||||
_ struct{} `title:"Kairos Schema: Users block" description:"The users block allows you to create users in the system."`
|
||||
Name string `json:"name,omitempty" pattern:"([a-z_][a-z0-9_]{0,30})" required:"true" example:"kairos"`
|
||||
Passwd string `json:"passwd,omitempty" example:"kairos"`
|
||||
LockPasswd bool `json:"lockPasswd,omitempty" example:"true"`
|
||||
Groups []string `json:"groups,omitempty" example:"admin"`
|
||||
SSHAuthorizedKeys []string `json:"ssh_authorized_keys,omitempty" examples:"[\"github:USERNAME\",\"ssh-ed25519 AAAF00BA5\"]"`
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/kairos-io/kairos/v2/pkg/config/schemas"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Users Schema", func() {
|
||||
var config *KConfig
|
||||
var err error
|
||||
var yaml string
|
||||
|
||||
JustBeforeEach(func() {
|
||||
config, err = NewConfigFromYAML(yaml, UserSchema{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("When a user has no name", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
passwd: foobar`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(config.ValidationError.Error()).To(MatchRegexp("missing properties: 'name'"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("When a user name doesn't fit the pattern", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
name: "007"
|
||||
passwd: "bond"`
|
||||
})
|
||||
|
||||
It("errors", func() {
|
||||
Expect(config.IsValid()).NotTo(BeTrue())
|
||||
Expect(
|
||||
strings.Contains(config.ValidationError.Error(),
|
||||
"does not match pattern '([a-z_][a-z0-9_]{0,30})'",
|
||||
),
|
||||
).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With only the required attributes", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
name: "kairos"`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("With all possible attributes", func() {
|
||||
BeforeEach(func() {
|
||||
yaml = `#cloud-config
|
||||
name: "kairos"
|
||||
passwd: "kairos"
|
||||
lock_passwd: true
|
||||
groups:
|
||||
- "admin"
|
||||
ssh_authorized_keys:
|
||||
- github:mudler`
|
||||
})
|
||||
|
||||
It("succeeds", func() {
|
||||
Expect(config.IsValid()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
@ -1,63 +0,0 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/google/go-github/v40/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func newHTTPClient(ctx context.Context, token string) *http.Client {
|
||||
if token == "" {
|
||||
return http.DefaultClient
|
||||
}
|
||||
src := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
|
||||
return oauth2.NewClient(ctx, src)
|
||||
}
|
||||
|
||||
// FindReleases finds the releases from the given repo (slug) and returns a parsed semver.Collection
|
||||
// where the first item is the highest version as its sorted.
|
||||
func FindReleases(ctx context.Context, token, slug string, preReleases bool) (semver.Collection, error) {
|
||||
hc := newHTTPClient(ctx, token)
|
||||
cli := github.NewClient(hc)
|
||||
|
||||
repo := strings.Split(slug, "/")
|
||||
if len(repo) != 2 || repo[0] == "" || repo[1] == "" {
|
||||
return nil, fmt.Errorf("Invalid slug format. It should be 'owner/name': %s", slug)
|
||||
}
|
||||
|
||||
// Get at least 30 releases
|
||||
opts := github.ListOptions{PerPage: 30}
|
||||
rels, res, err := cli.Repositories.ListReleases(ctx, repo[0], repo[1], &opts)
|
||||
if err != nil {
|
||||
log.Println("API returned an error response:", err)
|
||||
if res != nil && res.StatusCode == 404 {
|
||||
// 404 means repository not found or release not found. It's not an error here.
|
||||
err = nil
|
||||
log.Println("API returned 404. Repository or release not found")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var versions semver.Collection
|
||||
for _, rel := range rels {
|
||||
if strings.HasPrefix(*rel.Name, "v") {
|
||||
v := semver.MustParse(*rel.Name)
|
||||
if v.Prerelease() == "" {
|
||||
versions = append(versions, v)
|
||||
}
|
||||
if v.Prerelease() != "" && preReleases {
|
||||
versions = append(versions, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return them reversed sorted so the higher is the first one in the collection!
|
||||
sort.Sort(sort.Reverse(versions))
|
||||
return versions, nil
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package github_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/kairos-io/kairos/v2/pkg/github"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestReleases(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Releases Suite")
|
||||
}
|
||||
|
||||
var _ = Describe("Releases", func() {
|
||||
It("can find the proper releases in order", func() {
|
||||
releases, err := github.FindReleases(context.Background(), "", "kairos-io/kairos", false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(releases)).To(BeNumerically(">", 0))
|
||||
// Expect the first one to be greater than the last one
|
||||
Expect(releases[0].GreaterThan(releases[len(releases)-1]))
|
||||
})
|
||||
It("can find the proper releases in order with prereleases", func() {
|
||||
releases, err := github.FindReleases(context.Background(), "", "kairos-io/kairos", true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(releases)).To(BeNumerically(">", 0))
|
||||
// Expect the first one to be greater than the last one
|
||||
Expect(releases[0].GreaterThan(releases[len(releases)-1]))
|
||||
})
|
||||
})
|
5
profile-build/README.md
Normal file
5
profile-build/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# profile-builder
|
||||
|
||||
--------------------
|
||||
|
||||
This is used in conjunction with the framework-profile files to install the required luet packages depending on the flavor
|
17
profile-build/go.mod
Normal file
17
profile-build/go.mod
Normal file
@ -0,0 +1,17 @@
|
||||
module main
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/kairos-io/kairos-sdk v0.0.1
|
||||
github.com/urfave/cli v1.22.12
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/denisbrodbeck/machineid v1.0.1 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
33
profile-build/go.sum
Normal file
33
profile-build/go.sum
Normal file
@ -0,0 +1,33 @@
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kairos-io/kairos-sdk v0.0.1 h1:obJw0/5amn+/wWNuDTVq81HcPuX5TwHbGS4xxEy9Ju4=
|
||||
github.com/kairos-io/kairos-sdk v0.0.1/go.mod h1:E70cYgGQpu1MXI8ddhH4CHVIvNi3w7l6MQlxLTeBTXY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
|
||||
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
34
profile-build/main.go
Normal file
34
profile-build/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/kairos-io/kairos-sdk/profile"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var VERSION = "0.0.0"
|
||||
|
||||
func main() {
|
||||
|
||||
app := &cli.App{
|
||||
Name: "profile-build",
|
||||
Version: VERSION,
|
||||
Authors: []cli.Author{{Name: "Kairos authors"}},
|
||||
Usage: "Build kairos framework images",
|
||||
Description: `Uses profile files to build kairos images`,
|
||||
UsageText: ``,
|
||||
Copyright: "kairos authors",
|
||||
ArgsUsage: "flavor profileName profileFile outputDirectory",
|
||||
Action: func(c *cli.Context) error {
|
||||
return profile.BuildFlavor(c.Args().Get(0), c.Args().Get(1), c.Args().Get(2))
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
41
tests/go.mod
Normal file
41
tests/go.mod
Normal file
@ -0,0 +1,41 @@
|
||||
module github.com/kairos-io/kairos/v2
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d
|
||||
github.com/onsi/ginkgo/v2 v2.9.2
|
||||
github.com/onsi/gomega v1.27.6
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1 // indirect
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 // indirect
|
||||
github.com/codingsince1985/checksum v1.2.6 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect
|
||||
github.com/ipfs/go-log v1.0.5 // indirect
|
||||
github.com/ipfs/go-log/v2 v2.5.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.7.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/term v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.7.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
216
tests/go.sum
Normal file
216
tests/go.sum
Normal file
@ -0,0 +1,216 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1 h1:BKTqrqXiQYovrDlfuVFaEGz0r4Ou6EED8L7jCXw6Buw=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yPXQTPshACY8vQ0=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
|
||||
github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4=
|
||||
github.com/codingsince1985/checksum v1.2.6 h1:UjCDls6oaRQeLPG14TvjLvOos2XL1qHdMl8uGMkzpi8=
|
||||
github.com/codingsince1985/checksum v1.2.6/go.mod h1:Pe5wfeiqzQC1qEXLWEFmxQ3W/OklJEJGiJO62graCJU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
|
||||
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8=
|
||||
github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo=
|
||||
github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g=
|
||||
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
|
||||
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d h1:/lAg9vPAAU+s35cDMCx1IyeMn+4OYfCBPqi08Q8vXDg=
|
||||
github.com/mudler/go-processmanager v0.0.0-20220724164624-c45b5c61312d/go.mod h1:HGGAOJhipApckwNV8ZTliRJqxctUv3xRY+zbQEwuytc=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
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.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46 h1:q2T2RnISqPdZWvUpBQw0n7QWtF4cNo5RpCDTZmV732M=
|
||||
github.com/spectrocloud/peg v0.0.0-20230407121159-2e15270c4a46/go.mod h1:L2fIdtZqbQEagjOOXwkwH3t7MjJUd7fbt52cLSQGDBg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
Loading…
Reference in New Issue
Block a user