Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/pkg: Apply operation #63

Merged
merged 10 commits into from
May 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions cmd/pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import (
"github.com/vanilla-os/orchid/cmdr"
)

var validPkgArgs = []string{"add", "remove", "list"}
var validPkgArgs = []string{"add", "remove", "list", "apply"}

func NewPkgCommand() *cmdr.Command {
cmd := cmdr.NewCommand(
"pkg add|remove|list",
"pkg add|remove|list|apply",
abroot.Trans("pkg.long"),
abroot.Trans("pkg.short"),
pkg,
Expand Down Expand Up @@ -88,6 +88,18 @@ func pkg(cmd *cobra.Command, args []string) error {

cmdr.Info.Printf(abroot.Trans("pkg.listMsg"), added, removed)
return nil
case "apply":
aBsys, err := core.NewABSystem()
if err != nil {
cmdr.Error.Println(err)
return err
}

err = aBsys.RunOperation(core.APPLY)
if err != nil {
cmdr.Info.Println(abroot.Trans("pkg.applyFailed"))
return err
}
}

return nil
Expand Down
9 changes: 8 additions & 1 deletion cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,14 @@ func upgrade(cmd *cobra.Command, args []string) error {
return err
}

err = aBsys.Upgrade(force)
var operation core.ABSystemOperation
if force {
operation = core.FOCE_UPGRADE
} else {
operation = core.UPGRADE
}

err = aBsys.RunOperation(operation)
if err != nil {
if err == core.NoUpdateError {
cmdr.Info.Println(abroot.Trans("upgrade.noUpdateAvailable"))
Expand Down
82 changes: 81 additions & 1 deletion core/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@ package core
*/

import (
"context"
"errors"
"os"
"path/filepath"

"github.com/containers/buildah"
"github.com/vanilla-os/abroot/settings"
"github.com/vanilla-os/prometheus"
)

// GenerateRootfs generates a rootfs from a image recipe file
// OciExportRootFs generates a rootfs from a image recipe file
func OciExportRootFs(buildImageName string, imageRecipe *ImageRecipe, transDir string, dest string) error {
PrintVerbose("OciExportRootFs: running...")

Expand Down Expand Up @@ -105,3 +107,81 @@ func OciExportRootFs(buildImageName string, imageRecipe *ImageRecipe, transDir s

return nil
}

// FindImageWithLabel returns the name of the first image containinig the provided key-value pair
// or an empty string if none was found
func FindImageWithLabel(key, value string) (string, error) {
PrintVerbose("FindImageWithLabel: running...")

pt, err := prometheus.NewPrometheus(
"/var/lib/abroot/storage",
"overlay",
settings.Cnf.MaxParallelDownloads,
)
if err != nil {
PrintVerbose("FindImageWithLabel:err: %s", err)
return "", err
}

images, err := pt.Store.Images()
if err != nil {
PrintVerbose("FindImageWithLabel:err(2): %s", err)
return "", err
}

for _, img := range images {
// This is the only way I could find to get the labels form an image
builder, err := buildah.ImportBuilderFromImage(context.Background(), pt.Store, buildah.ImportFromImageOptions{Image: img.ID})
if err != nil {
PrintVerbose("FindImageWithLabel:err(3): %s", err)
return "", err
}

val, ok := builder.Labels()[key]
if ok && val == value {
return img.Names[0], nil
}
}

return "", nil
}

// DeleteImageForRoot retrieves the image created for the provided root ("vos-a"|"vos-b")
func RetrieveImageForRoot(root string) (string, error) {
PrintVerbose("ApplyInImageForRoot: running...")

image, err := FindImageWithLabel("ABRoot.root", root)
if err != nil {
PrintVerbose("ApplyInImageForRoot:err: %s", err)
return "", err
}

return image, nil
}

// DeleteImageForRoot deletes the image created for the provided root ("vos-a"|"vos-b")
func DeleteImageForRoot(root string) error {
image, err := RetrieveImageForRoot(root)
if err != nil {
PrintVerbose("DeleteImageForRoot:err: %s", err)
return err
}

pt, err := prometheus.NewPrometheus(
"/var/lib/abroot/storage",
"overlay",
settings.Cnf.MaxParallelDownloads,
)
if err != nil {
PrintVerbose("DeleteImageForRoot:err(2): %s", err)
return err
}

_, err = pt.Store.DeleteImage(image, true)
if err != nil {
PrintVerbose("DeleteImageForRoot:err(3): %s", err)
return err
}

return nil
}
169 changes: 147 additions & 22 deletions core/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,32 @@ import (
type PackageManager struct{}

const (
PackagesBaseDir = "/etc/abroot"
PackagesAddFile = "packages.add"
PackagesRemoveFile = "packages.remove"
PackagesBaseDir = "/etc/abroot"
PackagesAddFile = "packages.add"
PackagesRemoveFile = "packages.remove"
PackagesUnstagedFile = "packages.unstaged"
)

const (
ADD = "+"
REMOVE = "-"
)

// An unstaged package is a package that is waiting to be applied
// to the next root.
//
// Every time a `pkg apply` or `upgrade` operation
// is executed, all unstaged packages are consumed and added/removed
// in the next root.
type UnstagedPackage struct {
Name, Status string
}

// NewPackageManager returns a new PackageManager struct
func NewPackageManager() *PackageManager {
PrintVerbose("PackageManager.NewPackageManager: running...")

err := os.MkdirAll(PackagesAddFile, 0755)
if err != nil {
PrintVerbose("PackageManager.NewPackageManager:err: " + err.Error())
panic(err)
}

err = os.MkdirAll(PackagesRemoveFile, 0755)
err := os.MkdirAll(PackagesBaseDir, 0755)
if err != nil {
PrintVerbose("PackageManager.NewPackageManager:err: " + err.Error())
panic(err)
Expand Down Expand Up @@ -81,11 +91,25 @@ func NewPackageManager() *PackageManager {
func (p *PackageManager) Add(pkg string) error {
PrintVerbose("PackageManager.Add: running...")

pkgs, err := p.GetAddPackages()
// Add to unstaged packages first
upkgs, err := p.GetUnstagedPackages()
if err != nil {
PrintVerbose("PackageManager.Add:err: " + err.Error())
return err
}
upkgs = append(upkgs, UnstagedPackage{pkg, ADD})
err = p.writeUnstagedPackages(upkgs)
if err != nil {
PrintVerbose("PackageManager.Add:err(2): " + err.Error())
return err
}

// Modify added packages list
pkgs, err := p.GetAddPackages()
if err != nil {
PrintVerbose("PackageManager.Add:err(3): " + err.Error())
return err
}

for _, p := range pkgs {
if p == pkg {
Expand All @@ -104,19 +128,35 @@ func (p *PackageManager) Add(pkg string) error {
func (p *PackageManager) Remove(pkg string) error {
PrintVerbose("PackageManager.Remove: running...")

apkgs, err := p.GetAddPackages()
// Add to unstaged packages first
upkgs, err := p.GetUnstagedPackages()
if err != nil {
PrintVerbose("PackageManager.Add:err: " + err.Error())
return err
}
upkgs = append(upkgs, UnstagedPackage{pkg, REMOVE})
err = p.writeUnstagedPackages(upkgs)
if err != nil {
PrintVerbose("PackageManager.Remove:err: " + err.Error())
PrintVerbose("PackageManager.Remove:err(2): " + err.Error())
return err
}

for i, apkg := range apkgs {
if apkg == pkg {
apkgs = append(apkgs[:i], apkgs[i+1:]...)
return p.writeAddPackages(apkgs)
// If package was added by the user, simply remove it from packages.add
// Unstaged will take care of the rest
pkgs, err := p.GetAddPackages()
if err != nil {
PrintVerbose("PackageManager.Remove:err(3): " + err.Error())
return err
}
for i, ap := range pkgs {
if ap == pkg {
pkgs = append(pkgs[:i], pkgs[i+1:]...)
PrintVerbose("PackageManager.Remove: removing manually added package")
return p.writeAddPackages(pkgs)
}
}

// Otherwise, add package to packages.remove
PrintVerbose("PackageManager.Remove: writing packages.remove")
return p.writeRemovePackages(pkg)
}
Expand All @@ -133,6 +173,34 @@ func (p *PackageManager) GetRemovePackages() ([]string, error) {
return p.getPackages(PackagesRemoveFile)
}

// GetUnstagedPackages returns the package changes that are yet to be applied
func (p *PackageManager) GetUnstagedPackages() ([]UnstagedPackage, error) {
PrintVerbose("PackageManager.GetUnstagedPackages: running...")
pkgs, err := p.getPackages(PackagesUnstagedFile)
if err != nil {
PrintVerbose("PackageManager.GetUnstagedPackages:err: ", err.Error())
return nil, err
}

unstagedList := []UnstagedPackage{}
for _, line := range pkgs {
if line == "" || line == "\n" {
continue
}

splits := strings.SplitN(line, " ", 2)
unstagedList = append(unstagedList, UnstagedPackage{splits[1], splits[0]})
}

return unstagedList, nil
}

// ClearUnstagedPackages removes all packages from the unstaged list
func (p *PackageManager) ClearUnstagedPackages() error {
PrintVerbose("PackageManager.ClearUnstagedPackages: running...")
return p.writeUnstagedPackages([]UnstagedPackage{})
}

// GetAddPackages returns the packages in the packages.add file as string
func (p *PackageManager) GetAddPackagesString(sep string) (string, error) {
PrintVerbose("PackageManager.GetAddPackagesString: running...")
Expand Down Expand Up @@ -209,6 +277,17 @@ func (p *PackageManager) writeRemovePackages(pkg string) error {
return p.writePackages(PackagesRemoveFile, pkgs)
}

func (p *PackageManager) writeUnstagedPackages(pkgs []UnstagedPackage) error {
PrintVerbose("PackageManager.writeUnstagedPackages: running...")

pkgFmt := []string{}
for _, pkg := range pkgs {
pkgFmt = append(pkgFmt, fmt.Sprintf("%s %s", pkg.Status, pkg.Name))
}

return p.writePackages(PackagesUnstagedFile, pkgFmt)
}

func (p *PackageManager) writePackages(file string, pkgs []string) error {
PrintVerbose("PackageManager.writePackages: running...")

Expand All @@ -235,24 +314,52 @@ func (p *PackageManager) writePackages(file string, pkgs []string) error {
return nil
}

func (p *PackageManager) GetFinalCmd() string {
PrintVerbose("PackageManager.GetFinalCmd: running...")
func (p *PackageManager) processApplyPackages() (string, string) {
PrintVerbose("PackageManager.processApplyPackages: running...")

unstaged, err := p.GetUnstagedPackages()
if err != nil {
PrintVerbose("PackageManager.processApplyPackages:err: %s", err.Error())
}

var addPkgs, removePkgs []string
for _, pkg := range unstaged {
if pkg.Status == ADD {
addPkgs = append(addPkgs, pkg.Name)
} else if pkg.Status == REMOVE {
removePkgs = append(removePkgs, pkg.Name)
}
}

finalAddPkgs := ""
if len(addPkgs) > 0 {
finalAddPkgs = fmt.Sprintf("%s %s", settings.Cnf.IPkgMngAdd, strings.Join(addPkgs, " "))
}

finalRemovePkgs := ""
if len(removePkgs) > 0 {
finalRemovePkgs = fmt.Sprintf("%s %s", settings.Cnf.IPkgMngRm, strings.Join(removePkgs, " "))
}

return finalAddPkgs, finalRemovePkgs
}

func (p *PackageManager) processUpgradePackages() (string, string) {
addPkgs, err := p.GetAddPackagesString(" ")
if err != nil {
PrintVerbose("PackageManager.GetFinalCmd:err: " + err.Error())
return ""
return "", ""
}

removePkgs, err := p.GetRemovePackagesString(" ")
if err != nil {
PrintVerbose("PackageManager.GetFinalCmd:err(2): " + err.Error())
return ""
return "", ""
}

if len(addPkgs) == 0 && len(removePkgs) == 0 {
PrintVerbose("PackageManager.GetFinalCmd: no packages to install or remove")
return "true"
return "", ""
}

finalAddPkgs := ""
Expand All @@ -265,6 +372,24 @@ func (p *PackageManager) GetFinalCmd() string {
finalRemovePkgs = fmt.Sprintf("%s %s", settings.Cnf.IPkgMngRm, removePkgs)
}

return finalAddPkgs, finalRemovePkgs
}

func (p *PackageManager) GetFinalCmd(operation ABSystemOperation) string {
PrintVerbose("PackageManager.GetFinalCmd: running...")

var finalAddPkgs, finalRemovePkgs string
if operation == APPLY {
finalAddPkgs, finalRemovePkgs = p.processApplyPackages()
} else {
err := p.ClearUnstagedPackages()
if err != nil {
PrintVerbose("PackageManager.GetFinalCmd:err: %s", err.Error())
panic(err)
}
finalAddPkgs, finalRemovePkgs = p.processUpgradePackages()
}

cmd := ""
if finalAddPkgs != "" && finalRemovePkgs != "" {
cmd = fmt.Sprintf("%s && %s", finalAddPkgs, finalRemovePkgs)
Expand Down
Loading