github.com/flanksource/konfigadm@v0.12.0/pkg/phases/packages.go (about)

     1  package phases
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  	"strings"
     7  
     8  	log "github.com/sirupsen/logrus"
     9  
    10  	"github.com/flanksource/konfigadm/pkg/types"
    11  	"github.com/flanksource/konfigadm/pkg/utils"
    12  )
    13  
    14  var Packages types.AllPhases = packages{}
    15  
    16  type packages struct{}
    17  
    18  func (p packages) ApplyPhase(sys *types.Config, ctx *types.SystemContext) ([]types.Command, types.Filesystem, error) {
    19  	commands := types.Commands{}
    20  	files := types.Filesystem{}
    21  
    22  	for _, repo := range *sys.PackageRepos {
    23  		var os OS
    24  		var err error
    25  		if os, err = GetOSForTag(repo.Flags...); err != nil {
    26  			return nil, nil, err
    27  		}
    28  
    29  		if repo.URL != "" || repo.ExtraArgs["mirrorlist"] != "" {
    30  			_commands := os.GetPackageManager().
    31  				AddRepo(repo.URL, repo.Channel, repo.VersionCodeName, repo.Name, repo.GPGKey, repo.ExtraArgs)
    32  			commands.Append(_commands.WithTags(repo.Flags...))
    33  		}
    34  	}
    35  	if len(sys.TarPackages) > 0 {
    36  		sys.AddPackage("tar", nil)
    37  		sys.AddPackage("wget", nil)
    38  	}
    39  	addPackageCommands(sys, &commands)
    40  
    41  	for _, tar := range sys.TarPackages {
    42  		filename := filepath.Base(tar.URL)
    43  		commands.Add(fmt.Sprintf("wget -O %s -nv %s", filename, tar.URL))
    44  		if tar.Checksum != "" {
    45  			tar.ChecksumType = strings.TrimSuffix(tar.ChecksumType, "sum")
    46  			commands.Add(fmt.Sprintf("echo %s | %ssum --check", tar.Checksum, tar.ChecksumType))
    47  		}
    48  		commands.Add(extractTo(filename, tar.Destination)).
    49  			Add(fmt.Sprintf("rm %s", filename))
    50  	}
    51  
    52  	_commands := commands.Merge()
    53  	return _commands, files, nil
    54  }
    55  
    56  func extractTo(filename, destination string) string {
    57  	switch {
    58  	case strings.HasSuffix(filename, "tar.gz"), strings.HasSuffix(filename, "tgz"):
    59  		return fmt.Sprintf("tar -zxf %s -C %s", filename, destination)
    60  	}
    61  	return fmt.Sprintf("mv %s %s", filename, destination)
    62  }
    63  
    64  type packageOperations struct {
    65  	install   []string
    66  	uninstall []string
    67  	mark      []string
    68  	tags      []types.Flag
    69  }
    70  
    71  func appendStrings(slice []string, s string) []string {
    72  	var newSlice []string
    73  	if slice != nil {
    74  		newSlice = slice
    75  	}
    76  	newSlice = append(newSlice, s)
    77  	return newSlice
    78  }
    79  
    80  func getKeyFromTags(tags ...types.Flag) string {
    81  	return fmt.Sprintf("%s", tags)
    82  }
    83  
    84  func addPackageCommands(sys *types.Config, commands *types.Commands) {
    85  	// package installation can have 2 scenarios:
    86  	// 1) tags specified and we know the package manager
    87  	// 2) tags not specified, so we need to add tagged commands for each base operating system
    88  
    89  	// track operations by tag group
    90  	// TODO merge compatible tags, e.g. ubuntu and debian-like tags can be included in the same command
    91  	var managers = make(map[string]packageOperations)
    92  
    93  	// handle case 1) tags not specified
    94  	for _, p := range *sys.Packages {
    95  		if len(p.Flags) == 0 {
    96  			continue
    97  		}
    98  
    99  		var ops packageOperations
   100  		var ok bool
   101  		if ops, ok = managers[getKeyFromTags(p.Flags...)]; !ok {
   102  			ops = packageOperations{tags: p.Flags}
   103  		}
   104  		if p.Uninstall {
   105  			ops.uninstall = appendStrings(ops.uninstall, p.Name)
   106  		} else {
   107  			ops.install = appendStrings(ops.install, p.VersionedName())
   108  		}
   109  		if p.Mark {
   110  			ops.mark = appendStrings(ops.mark, p.Name)
   111  		}
   112  
   113  		managers[getKeyFromTags(p.Flags...)] = ops
   114  	}
   115  
   116  	// handle case 2) tags specified
   117  	for _, os := range BaseOperatingSystems {
   118  		for _, p := range *sys.Packages {
   119  			if len(p.Flags) > 0 {
   120  				continue
   121  			}
   122  			var ops packageOperations
   123  			var ok bool
   124  			if ops, ok = managers[os.GetName()]; !ok {
   125  				ops = packageOperations{tags: []types.Flag{*types.GetTag(os.GetName())}}
   126  			}
   127  			if p.Uninstall {
   128  				ops.uninstall = appendStrings(ops.uninstall, p.Name)
   129  			} else {
   130  				ops.install = appendStrings(ops.install, p.VersionedName())
   131  			}
   132  			if p.Mark {
   133  				ops.mark = appendStrings(ops.mark, p.Name)
   134  			}
   135  			managers[os.GetName()] = ops
   136  		}
   137  	}
   138  
   139  	// iterate over all tag/op combinations and emit commands
   140  	for _, ops := range managers {
   141  		os, _ := GetOSForTag(ops.tags...)
   142  		commands.Append(os.GetPackageManager().Update().WithTags(ops.tags...))
   143  
   144  		if ops.install != nil && len(ops.install) > 0 {
   145  			commands = commands.Append(os.GetPackageManager().Install(ops.install...).WithTags(ops.tags...))
   146  		}
   147  		if ops.uninstall != nil && len(ops.uninstall) > 0 {
   148  			commands = commands.Append(os.GetPackageManager().Uninstall(ops.uninstall...).WithTags(ops.tags...))
   149  		}
   150  		if ops.mark != nil && len(ops.mark) > 0 {
   151  			commands = commands.Append(os.GetPackageManager().Mark(ops.mark...).WithTags(ops.tags...))
   152  		}
   153  	}
   154  
   155  }
   156  
   157  func (p packages) ProcessFlags(sys *types.Config, flags ...types.Flag) {
   158  	minified := []types.Package{}
   159  	for _, pkg := range *sys.Packages {
   160  		if types.MatchAll(flags, pkg.Flags) {
   161  			minified = append(minified, pkg)
   162  		}
   163  	}
   164  	sys.Packages = &minified
   165  
   166  	minifiedRepos := []types.PackageRepo{}
   167  	for _, repo := range *sys.PackageRepos {
   168  		if types.MatchesAny(flags, repo.Flags) {
   169  			minifiedRepos = append(minifiedRepos, repo)
   170  		}
   171  	}
   172  	sys.PackageRepos = &minifiedRepos
   173  }
   174  
   175  func (p packages) Verify(cfg *types.Config, results *types.VerifyResults, flags ...types.Flag) bool {
   176  	verify := true
   177  	var os OS
   178  	var err error
   179  	if os, err = GetOSForTag(flags...); err != nil {
   180  		results.Fail("Unable to find OS for tags %s", flags)
   181  		return false
   182  	}
   183  
   184  	for _, p := range *cfg.Packages {
   185  		if !types.MatchesAny(flags, p.Flags) {
   186  			continue
   187  		}
   188  		expandedVersion, _ := utils.SafeExec("echo %s", p.Version)
   189  		expandedVersion = strings.Replace(expandedVersion, "\n", "", -1)
   190  		log.Tracef("Verifying package: %s, version: %s => %s", p.Name, p.Version, expandedVersion)
   191  		installed := os.GetPackageManager().GetInstalledVersion(p.Name)
   192  		if p.Uninstall {
   193  			if installed == "" {
   194  				results.Pass("%s is not installed", p)
   195  			} else {
   196  				results.Fail("%s should not be installed", p)
   197  				verify = false
   198  			}
   199  		} else if p.Version == "" && installed != "" {
   200  			results.Pass("%s is installed with any version: %s", p, installed)
   201  		} else if p.Version == "" && installed == "" {
   202  			results.Fail("%s is not installed, any version required", p)
   203  			verify = false
   204  		} else if strings.HasPrefix(expandedVersion, installed) || strings.HasPrefix(installed, expandedVersion) {
   205  			results.Pass("%s is installed with expected version: %s", p, installed)
   206  		} else {
   207  			results.Fail("%s is installed, but expected %s, got %s", p.Name, expandedVersion, installed)
   208  			verify = false
   209  		}
   210  	}
   211  
   212  	return verify
   213  }