github.com/flanksource/konfigadm@v0.12.0/pkg/phases/kernel.go (about) 1 package phases 2 3 import ( 4 "fmt" 5 "regexp" 6 "strings" 7 8 "github.com/flanksource/konfigadm/pkg/types" 9 "github.com/flanksource/konfigadm/pkg/utils" 10 ) 11 12 var Kernel types.AllPhases = kernel{} 13 14 type kernel struct{} 15 16 func (p kernel) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) { 17 commands := types.Commands{} 18 files := types.Filesystem{} 19 var os OS 20 var err error 21 if len(*sys.Kernel) == 0 { 22 return []types.Command{}, files, nil 23 } 24 // Ensure repo caches are up to date before trying to install kernel 25 for _, os := range SupportedOperatingSystems { 26 commands = *commands.Append(os.GetPackageManager().Update().WithTags(os.GetTags()...)) 27 } 28 for _, kern := range *sys.Kernel { 29 var oslist []OS 30 if len(kern.Flags) > 0 { 31 // Kernel version is tagged for a specific OS (family) 32 // Add specific kernel and headers package 33 for _, flag := range kern.Flags { 34 if os, err = GetOSForTag(flag); err != nil { 35 continue 36 } 37 oslist = append(oslist, os) 38 } 39 } else { 40 // Input is not tagged 41 // Add for all supported OSes 42 oslist = SupportedOperatingSystems 43 } 44 for _, os := range oslist { 45 commandtags := os.GetTags() 46 // rpm based systems will fail to install kernels in containers 47 if types.MatchesAny(commandtags, []types.Flag{types.FEDORA}) { 48 commandtags = append(commandtags, types.NOT_CONTAINER) 49 } 50 kernelname, kernelheadername := os.GetKernelPackageNames(kern.Version) 51 commands = *commands.Append(os.GetPackageManager().Install(kernelname, kernelheadername).WithTags(commandtags...)) 52 commands = *commands.Append(os.GetPackageManager().Mark(kernelname, kernelheadername).WithTags(commandtags...)) 53 commands = *commands.Append(os.UpdateDefaultKernel(kern.Version).WithTags(append(os.GetTags(), types.NOT_CONTAINER)...)) 54 55 } 56 } 57 return commands.Merge(), files, nil 58 } 59 60 func (p kernel) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool { 61 verify := true 62 var os OS 63 var err error 64 if os, err = GetOSForTag(flags...); err != nil { 65 results.Fail("Unable to find OS for tags %s", flags) 66 return false 67 } 68 if len(*cfg.Kernel) == 0 { 69 return verify 70 } 71 incontainer := types.MatchesAny(flags, []types.Flag{types.CONTAINER}) 72 // dockerskip := types.MatchesAny(flags, []types.Flag{types.FEDORA}) 73 dockerskip := false 74 // An improperly configured input (eg different tags for both redhatlike and 75 // centos then run on centos) will fail to verify since only one of the 76 // two kernels can be the default 77 for _, kern := range *cfg.Kernel { 78 kernelpackage, kernelheaderpackage := os.GetKernelPackageNames(kern.Version) 79 // Certain containers will skip kernel install, skip verification 80 if !(incontainer && dockerskip) { 81 installed := os.GetPackageManager().GetInstalledVersion(kernelpackage) 82 if installed != "" { 83 results.Pass("%s is installed", kernelpackage) 84 } else { 85 results.Fail("%s is not installed", kernelpackage) 86 verify = verify && false 87 } 88 installed = os.GetPackageManager().GetInstalledVersion(kernelheaderpackage) 89 if installed != "" { 90 results.Pass("%s is installed", kernelheaderpackage) 91 } else { 92 results.Fail("%s is not installed", kernelheaderpackage) 93 verify = verify && false 94 } 95 } else { 96 results.Skip("Cannot test inside a container") 97 } 98 // Ignore grub config in containers 99 if !incontainer { 100 if os.ReadDefaultKernel() == kern.Version { 101 results.Pass("%s is the default kernel", kern.Version) 102 } else { 103 results.Fail("%s is not the default kernel", kern.Version) 104 verify = verify && false 105 } 106 } else { 107 results.Skip("Cannot test inside a container") 108 } 109 } 110 111 return verify 112 } 113 114 func (p kernel) ProcessFlags(sys *types.Config, flags ...types.Flag) { 115 minified := []types.KernelInput{} 116 for _, pkg := range *sys.Kernel { 117 if types.MatchesAny(flags, pkg.Flags) { 118 minified = append(minified, pkg) 119 } 120 } 121 sys.Kernel = &minified 122 } 123 124 // Interface and corresponding structs for reading/writing grub config with grub.conf or grubby 125 type GrubConfigManager interface { 126 ReadDefaultKernel() (string, bool) 127 UpdateDefaultKernel(version string) types.Commands 128 } 129 130 // Generally used by yum systems 131 type GrubbyManager struct{} 132 133 func (p GrubbyManager) ReadDefaultKernel() (string, bool) { 134 grubbyout, ok := utils.SafeExec("grubby --default-kernel") 135 if ok { 136 // Strip dist and arch flags 137 for i := 0; i < 2; i++ { 138 grubbyout = grubbyout[:strings.LastIndex(grubbyout, ".")] 139 } 140 re := regexp.MustCompile("/boot/vmlinuz-(.*)") 141 match := re.FindStringSubmatch(grubbyout) 142 if len(match) > 1 { 143 return match[1], ok 144 } 145 return "", ok 146 } 147 return "", ok 148 } 149 150 func (p GrubbyManager) UpdateDefaultKernel(version string) types.Commands { 151 commands := types.Commands{} 152 commands.Add(fmt.Sprintf("grubby --set-default=/boot/vmlinuz-%s$(rpm --eval %%{dist}).$(uname -p)", version)) 153 return commands 154 } 155 156 // Generally used by apt systems 157 type GrubConfManager struct{} 158 159 func (p GrubConfManager) ReadDefaultKernel() (string, bool) { 160 return utils.SafeExec("cat /etc/default/grub | grep '^GRUB_DEFAULT=' | cut -d '=' -f 2") 161 } 162 163 func (p GrubConfManager) UpdateDefaultKernel(version string) types.Commands { 164 commands := types.Commands{} 165 commands.Add(fmt.Sprintf("sed 's/^GRUB_DEFAULT=.*/GRUB_DEFAULT=\"%s\"/' -i /etc/default/grub", version)) 166 commands.Add("update-grub") 167 return commands 168 }