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  }