1
0
out-of-tree/cmd/kernel.go

411 lines
9.0 KiB
Go
Raw Normal View History

// Copyright 2023 Mikhail Klementev. All rights reserved.
2018-11-17 20:24:09 +00:00
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
2024-02-17 22:38:43 +00:00
package cmd
2018-11-17 20:24:09 +00:00
import (
"errors"
"fmt"
"os"
"strings"
"time"
"github.com/naoina/toml"
"github.com/remeh/sizedwaitgroup"
2023-03-18 21:30:07 +00:00
"github.com/rs/zerolog/log"
2024-02-20 13:25:31 +00:00
"code.dumpstack.io/tools/out-of-tree/artifact"
"code.dumpstack.io/tools/out-of-tree/config"
2024-02-20 13:25:31 +00:00
"code.dumpstack.io/tools/out-of-tree/config/dotfiles"
"code.dumpstack.io/tools/out-of-tree/container"
"code.dumpstack.io/tools/out-of-tree/distro"
"code.dumpstack.io/tools/out-of-tree/kernel"
2018-11-17 20:24:09 +00:00
)
2023-01-31 07:13:33 +00:00
type KernelCmd struct {
NoDownload bool `help:"do not download qemu image while kernel generation"`
UseHost bool `help:"also use host kernels"`
Force bool `help:"force reinstall kernel"`
NoHeaders bool `help:"do not install kernel headers"`
Shuffle bool `help:"randomize kernels installation order"`
Retries int `help:"amount of tries for each kernel" default:"2"`
Threads int `help:"threads for parallel installation" default:"1"`
Update bool `help:"update container"`
PrebuiltContainers bool `help:"try prebuilt container images first" default:"true" negatable:""`
Max int `help:"maximum kernels to download" default:"100500"`
NoPrune bool `help:"do not remove dangling or unused images from local storage after build"`
NoCfgRegen bool `help:"do not update kernels.toml"`
2023-01-31 07:13:33 +00:00
ContainerTimeout time.Duration `help:"container timeout"`
2024-10-07 22:06:04 +00:00
RealtimeOutput RealtimeContainerOutputFlag `help:"show realtime output"`
2023-03-24 04:20:28 +00:00
List KernelListCmd `cmd:"" help:"list kernels"`
2023-05-10 07:52:44 +00:00
ListRemote KernelListRemoteCmd `cmd:"" help:"list remote kernels"`
2023-03-24 04:20:28 +00:00
Autogen KernelAutogenCmd `cmd:"" help:"generate kernels based on the current config"`
Genall KernelGenallCmd `cmd:"" help:"generate all kernels for distro"`
Install KernelInstallCmd `cmd:"" help:"install specific kernel"`
ConfigRegen KernelConfigRegenCmd `cmd:"" help:"regenerate config"`
2023-05-24 16:13:12 +00:00
shutdown bool
kcfg config.KernelConfig
stats struct {
overall int
success int
}
2023-01-31 07:13:33 +00:00
}
func (cmd KernelCmd) UpdateConfig() (err error) {
if cmd.stats.success != cmd.stats.overall {
log.Warn().Msgf("%d kernels failed to install",
cmd.stats.overall-cmd.stats.success)
}
2024-02-21 09:28:49 +00:00
if cmd.NoCfgRegen {
log.Info().Msgf("kernels.toml is not updated")
2024-02-21 09:34:28 +00:00
return
2024-02-21 09:28:49 +00:00
}
log.Info().Msgf("updating kernels.toml")
kcfg := config.KernelConfig{}
if cmd.UseHost {
// Get host kernels
kcfg.Kernels, err = kernel.GenHostKernels(!cmd.NoDownload)
if err != nil {
return
}
}
for _, dist := range distro.List() {
var kernels []distro.KernelInfo
kernels, err = dist.Kernels()
if err != nil {
return
}
kcfg.Kernels = append(kcfg.Kernels, kernels...)
}
buf, err := toml.Marshal(&kcfg)
if err != nil {
return
}
2024-02-20 13:25:31 +00:00
err = os.WriteFile(dotfiles.File("kernels.toml"), buf, os.ModePerm)
if err != nil {
return
}
log.Info().Msgf("kernels.toml successfully updated")
return
}
2024-02-20 13:25:31 +00:00
func (cmd *KernelCmd) GenKernel(km artifact.Target, pkg string) {
flog := log.With().
Str("kernel", pkg).
Str("distro", km.Distro.String()).
Logger()
reinstall := false
for _, kinfo := range cmd.kcfg.Kernels {
if !km.Distro.Equal(kinfo.Distro) {
continue
}
var found bool
if kinfo.Distro.ID == distro.Debian { // FIXME
found = pkg == kinfo.Package
2023-06-20 16:01:38 +00:00
} else if kinfo.Distro.ID == distro.OpenSUSE {
found = strings.Contains(pkg, kinfo.KernelRelease)
} else {
found = strings.Contains(pkg, kinfo.KernelVersion)
}
if found {
if !cmd.Force {
flog.Info().Msg("already installed")
return
}
reinstall = true
break
}
}
if reinstall {
flog.Info().Msg("reinstall")
} else {
flog.Info().Msg("install")
}
cmd.stats.overall += 1
2023-05-24 16:13:12 +00:00
var attempt int
for {
attempt++
if cmd.shutdown {
return
}
err := km.Distro.Install(pkg, !cmd.NoHeaders)
if err == nil {
cmd.stats.success += 1
flog.Info().Msg("success")
2023-05-24 16:13:12 +00:00
break
} else if attempt >= cmd.Retries {
flog.Error().Err(err).Msg("install kernel")
flog.Debug().Msg("skip")
2023-05-24 16:13:12 +00:00
break
} else {
flog.Warn().Err(err).Msg("install kernel")
2023-05-24 16:13:12 +00:00
time.Sleep(time.Second)
flog.Info().Msg("retry")
2023-05-24 16:13:12 +00:00
}
}
}
2024-02-20 13:25:31 +00:00
func (cmd *KernelCmd) Generate(g *Globals, km artifact.Target) (err error) {
2024-02-21 08:27:34 +00:00
defer func() {
if err != nil {
log.Warn().Err(err).Msg("")
} else {
log.Debug().Err(err).Msg("")
}
}()
2023-05-24 16:43:25 +00:00
if cmd.Update {
container.UseCache = false
2023-05-24 16:43:25 +00:00
}
if cmd.NoPrune {
container.Prune = false
}
container.UsePrebuilt = cmd.PrebuiltContainers
cmd.kcfg, err = config.ReadKernelConfig(g.Config.Kernels)
if err != nil {
log.Debug().Err(err).Msg("read kernels config")
}
container.Commands = g.Config.Docker.Commands
container.Registry = g.Config.Docker.Registry
container.Timeout = g.Config.Docker.Timeout.Duration
if cmd.ContainerTimeout != 0 {
container.Timeout = cmd.ContainerTimeout
}
log.Info().Msgf("Generating for target %v", km)
2023-06-15 15:24:29 +00:00
_, err = kernel.GenRootfsImage(km.Distro.RootFS(), !cmd.NoDownload)
2023-05-24 16:13:12 +00:00
if err != nil || cmd.shutdown {
return
}
pkgs, err := kernel.MatchPackages(km)
2023-05-24 16:13:12 +00:00
if err != nil || cmd.shutdown {
return
}
if cmd.Shuffle {
pkgs = kernel.ShuffleStrings(pkgs)
}
swg := sizedwaitgroup.New(cmd.Threads)
for i, pkg := range pkgs {
2023-05-24 16:13:12 +00:00
if cmd.shutdown {
err = nil
return
}
swg.Add()
2023-05-25 23:01:42 +00:00
if cmd.shutdown {
err = nil
swg.Done()
return
}
if cmd.stats.success >= cmd.Max {
2024-05-23 10:19:46 +00:00
log.Info().Msg("Max is reached")
swg.Done()
break
}
log.Info().Msgf("%d/%d %s", i+1, len(pkgs), pkg)
go func(p string) {
defer swg.Done()
cmd.GenKernel(km, p)
}(pkg)
}
swg.Wait()
return
}
2023-01-31 07:13:33 +00:00
type KernelListCmd struct{}
func (cmd *KernelListCmd) Run(g *Globals) (err error) {
kcfg, err := config.ReadKernelConfig(g.Config.Kernels)
if err != nil {
log.Debug().Err(err).Msg("read kernel config")
2023-01-31 07:13:33 +00:00
}
2018-11-17 20:24:09 +00:00
if len(kcfg.Kernels) == 0 {
2024-02-20 11:37:19 +00:00
return errors.New("no kernels found")
2018-11-17 20:24:09 +00:00
}
2023-01-31 07:13:33 +00:00
2018-11-17 20:24:09 +00:00
for _, k := range kcfg.Kernels {
fmt.Println(k.Distro.ID, k.Distro.Release, k.KernelRelease)
2018-11-17 20:24:09 +00:00
}
2023-01-31 07:13:33 +00:00
2018-11-17 20:24:09 +00:00
return
}
2023-05-10 07:52:44 +00:00
type KernelListRemoteCmd struct {
DistroID string `required:"" help:"distribution"`
DistroRelease string `help:"distro version"`
2023-05-10 07:52:44 +00:00
}
func (cmd *KernelListRemoteCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
if kernelCmd.Update {
container.UseCache = false
}
if kernelCmd.NoPrune {
container.Prune = false
}
container.UsePrebuilt = kernelCmd.PrebuiltContainers
distroType, err := distro.NewID(cmd.DistroID)
2023-05-10 07:52:44 +00:00
if err != nil {
return
}
2024-02-20 13:25:31 +00:00
km := artifact.Target{
Distro: distro.Distro{ID: distroType, Release: cmd.DistroRelease},
2024-02-20 13:25:31 +00:00
Kernel: artifact.Kernel{Regex: ".*"},
2023-05-10 07:52:44 +00:00
}
2023-06-15 15:24:29 +00:00
_, err = kernel.GenRootfsImage(km.Distro.RootFS(), false)
2023-05-10 07:52:44 +00:00
if err != nil {
return
}
container.Registry = g.Config.Docker.Registry
container.Commands = g.Config.Docker.Commands
2023-05-10 07:52:44 +00:00
pkgs, err := kernel.MatchPackages(km)
// error check skipped on purpose
2023-05-10 07:52:44 +00:00
for _, k := range pkgs {
fmt.Println(k)
}
return
}
type KernelAutogenCmd struct{}
2023-01-31 07:13:33 +00:00
func (cmd *KernelAutogenCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
2024-02-20 13:25:31 +00:00
ka, err := artifact.Artifact{}.Read(g.WorkDir + "/.out-of-tree.toml")
2023-01-31 07:13:33 +00:00
if err != nil {
return
}
2023-05-24 16:13:12 +00:00
kernel.SetSigintHandler(&kernelCmd.shutdown)
2023-04-07 20:52:45 +00:00
for _, sk := range ka.Targets {
if sk.Distro.Release == "" {
2024-02-20 11:37:19 +00:00
err = errors.New("please set distro_release")
2023-01-31 07:13:33 +00:00
return
}
err = kernelCmd.Generate(g, sk)
2023-01-31 07:13:33 +00:00
if err != nil {
return
}
2023-05-24 16:13:12 +00:00
if kernelCmd.shutdown {
2023-04-07 20:52:45 +00:00
break
}
2023-01-31 07:13:33 +00:00
}
return kernelCmd.UpdateConfig()
2023-01-31 07:13:33 +00:00
}
type KernelGenallCmd struct {
DistroID string `help:"distribution"`
DistroRelease string `help:"distro version"`
2023-01-31 07:13:33 +00:00
}
func (cmd *KernelGenallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
distroType, err := distro.NewID(cmd.DistroID)
2023-01-31 07:13:33 +00:00
if err != nil {
return
}
2023-05-24 16:13:12 +00:00
kernel.SetSigintHandler(&kernelCmd.shutdown)
2023-04-07 20:52:45 +00:00
for _, dist := range distro.List() {
2023-06-01 14:18:27 +00:00
if kernelCmd.shutdown {
break
}
if distroType != distro.None && distroType != dist.ID {
continue
}
if cmd.DistroRelease != "" && dist.Release != cmd.DistroRelease {
continue
}
2024-02-20 13:25:31 +00:00
target := artifact.Target{
Distro: dist,
2024-02-20 13:25:31 +00:00
Kernel: artifact.Kernel{Regex: ".*"},
}
err = kernelCmd.Generate(g, target)
if err != nil {
2024-02-21 08:27:34 +00:00
continue
}
2023-03-22 18:21:21 +00:00
}
return kernelCmd.UpdateConfig()
2023-03-22 18:21:21 +00:00
}
type KernelInstallCmd struct {
DistroID string `required:"" help:"distribution"`
DistroRelease string `required:"" help:"distro version"`
KernelRegex string `required:"" help:"kernel release mask"`
2023-03-22 18:21:21 +00:00
}
func (cmd *KernelInstallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
distroType, err := distro.NewID(cmd.DistroID)
2023-03-22 18:21:21 +00:00
if err != nil {
return
}
2023-05-24 16:13:12 +00:00
kernel.SetSigintHandler(&kernelCmd.shutdown)
2023-04-07 20:52:45 +00:00
2024-02-20 13:25:31 +00:00
km := artifact.Target{
Distro: distro.Distro{ID: distroType, Release: cmd.DistroRelease},
Kernel: artifact.Kernel{Regex: cmd.KernelRegex},
2023-03-22 18:21:21 +00:00
}
err = kernelCmd.Generate(g, km)
2023-01-31 07:13:33 +00:00
if err != nil {
return
}
return kernelCmd.UpdateConfig()
2023-01-31 07:13:33 +00:00
}
2023-03-24 04:20:28 +00:00
type KernelConfigRegenCmd struct{}
func (cmd *KernelConfigRegenCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
return kernelCmd.UpdateConfig()
}