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

feat: manage binary symlinks when changing context #102

Merged
merged 1 commit into from
Mar 11, 2024
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
148 changes: 136 additions & 12 deletions pkgmgr/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package pkgmgr

import (
"errors"
"fmt"
"io"
"io/fs"
Expand Down Expand Up @@ -283,6 +284,69 @@ func (p Package) uninstall(cfg Config, context string, keepData bool) error {
return nil
}

func (p Package) activate(cfg Config, context string) error {
pkgName := fmt.Sprintf("%s-%s-%s", p.Name, p.Version, context)
for _, installStep := range p.InstallSteps {
// Evaluate condition if defined
if installStep.Condition != "" {
if ok, err := cfg.Template.EvaluateCondition(installStep.Condition, nil); err != nil {
return NewInstallStepConditionError(installStep.Condition, err)
} else if !ok {
cfg.Logger.Debug(
fmt.Sprintf(
"skipping install step due to condition: %s",
installStep.Condition,
),
)
continue
}
}
if installStep.Docker != nil {
if err := installStep.Docker.activate(cfg, pkgName); err != nil {
return err
}
} else if installStep.File != nil {
if err := installStep.File.activate(cfg, pkgName); err != nil {
return err
}
} else {
return ErrNoInstallMethods
}
}
return nil
}

func (p Package) deactivate(cfg Config, context string) error {
pkgName := fmt.Sprintf("%s-%s-%s", p.Name, p.Version, context)
for _, installStep := range p.InstallSteps {
// Evaluate condition if defined
if installStep.Condition != "" {
if ok, err := cfg.Template.EvaluateCondition(installStep.Condition, nil); err != nil {
return NewInstallStepConditionError(installStep.Condition, err)
} else if !ok {
cfg.Logger.Debug(
fmt.Sprintf(
"skipping install step due to condition: %s",
installStep.Condition,
),
)
continue
}
}
if installStep.Docker != nil {
if err := installStep.Docker.deactivate(cfg, pkgName); err != nil {
return err
}
} else if installStep.File != nil {
if err := installStep.File.deactivate(cfg, pkgName); err != nil {
return err
}
} else {
return ErrNoInstallMethods
}
}
return nil
}
func (p Package) startService(cfg Config, context string) error {
pkgName := fmt.Sprintf("%s-%s-%s", p.Name, p.Version, context)

Expand Down Expand Up @@ -523,6 +587,16 @@ func (p *PackageInstallStepDocker) uninstall(cfg Config, pkgName string, keepDat
return nil
}

func (p *PackageInstallStepDocker) activate(cfg Config, pkgName string) error {
// Nothing to do
return nil
}

func (p *PackageInstallStepDocker) deactivate(cfg Config, pkgName string) error {
// Nothing to do
return nil
}

type PackageInstallStepFile struct {
Binary bool `yaml:"binary"`
Filename string `yaml:"filename"`
Expand Down Expand Up @@ -556,7 +630,35 @@ func (p *PackageInstallStepFile) install(cfg Config, pkgName string) error {
return err
}
cfg.Logger.Debug(fmt.Sprintf("wrote file %s", filePath))
return nil
}

func (p *PackageInstallStepFile) uninstall(cfg Config, pkgName string) error {
filePath := filepath.Join(
cfg.DataDir,
pkgName,
p.Filename,
)
cfg.Logger.Debug(fmt.Sprintf("deleting file %s", filePath))
if err := os.Remove(filePath); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
cfg.Logger.Warn(fmt.Sprintf("failed to remove file %s", filePath))
}
}
return nil
}

func (p *PackageInstallStepFile) activate(cfg Config, pkgName string) error {
if p.Binary {
tmpFilePath, err := cfg.Template.Render(p.Filename, nil)
if err != nil {
return err
}
filePath := filepath.Join(
cfg.DataDir,
pkgName,
p.Filename,
)
binPath := filepath.Join(
cfg.BinDir,
tmpFilePath,
Expand All @@ -565,6 +667,26 @@ func (p *PackageInstallStepFile) install(cfg Config, pkgName string) error {
if err := os.MkdirAll(parentDir, fs.ModePerm); err != nil {
return err
}
// Check for existing file at symlink location
if stat, err := os.Lstat(binPath); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return err
}
} else {
if (stat.Mode() & fs.ModeSymlink) > 0 {
// Remove existing symlink
if err := os.Remove(binPath); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return err
}
}
cfg.Logger.Debug(
fmt.Sprintf("removed existing symlink %q", binPath),
)
} else {
return fmt.Errorf("will not overwrite existing file %q with symlink", binPath)
}
}
if err := os.Symlink(filePath, binPath); err != nil {
return err
}
Expand All @@ -573,24 +695,26 @@ func (p *PackageInstallStepFile) install(cfg Config, pkgName string) error {
return nil
}

func (p *PackageInstallStepFile) uninstall(cfg Config, pkgName string) error {
filePath := filepath.Join(
cfg.DataDir,
pkgName,
p.Filename,
)
cfg.Logger.Debug(fmt.Sprintf("deleting file %s", filePath))
if err := os.Remove(filePath); err != nil {
cfg.Logger.Warn(fmt.Sprintf("failed to remove file %s", filePath))
}
func (p *PackageInstallStepFile) deactivate(cfg Config, pkgName string) error {
if p.Binary {
tmpFilePath, err := cfg.Template.Render(p.Filename, nil)
if err != nil {
return err
}
binPath := filepath.Join(
cfg.BinDir,
p.Filename,
tmpFilePath,
)
parentDir := filepath.Dir(binPath)
if err := os.MkdirAll(parentDir, fs.ModePerm); err != nil {
return err
}
if err := os.Remove(binPath); err != nil {
cfg.Logger.Warn(fmt.Sprintf("failed to remove symlink %s", binPath))
if !errors.Is(err, fs.ErrNotExist) {
return err
}
}
cfg.Logger.Debug(fmt.Sprintf("removed symlink %s", binPath))
}
return nil
}
41 changes: 41 additions & 0 deletions pkgmgr/pkgmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ func (p *PackageManager) Install(pkgs ...string) error {
notes,
)
}
// Activate package
if err := installPkg.Install.activate(p.config, activeContextName); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to activate package: %s", err),
)
}
}
// Display post-install notes
if notesOutput != "" {
Expand Down Expand Up @@ -230,6 +236,12 @@ func (p *PackageManager) Upgrade(pkgs ...string) error {
)
// Capture options from existing package
pkgOpts := upgradePkg.Installed.Options
// Deactivate old package
if err := upgradePkg.Installed.Package.deactivate(p.config, activeContextName); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to deactivate package: %s", err),
)
}
// Uninstall old version
if err := p.uninstallPackage(upgradePkg.Installed, true); err != nil {
return err
Expand All @@ -256,6 +268,12 @@ func (p *PackageManager) Upgrade(pkgs ...string) error {
if err := p.state.Save(); err != nil {
return err
}
// Activate new package
if err := upgradePkg.Upgrade.activate(p.config, activeContextName); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to activate package: %s", err),
)
}
}
// Display post-install notes
if notesOutput != "" {
Expand Down Expand Up @@ -306,6 +324,12 @@ func (p *PackageManager) Uninstall(keepData bool, pkgs ...string) error {
return err
}
for _, uninstallPkg := range uninstallPkgs {
// Deactivate package
if err := uninstallPkg.Package.deactivate(p.config, activeContextName); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to deactivate package: %s", err),
)
}
if err := p.uninstallPackage(uninstallPkg, keepData); err != nil {
return err
}
Expand Down Expand Up @@ -482,12 +506,29 @@ func (p *PackageManager) SetActiveContext(name string) error {
if _, ok := p.state.Contexts[name]; !ok {
return ErrContextNotExist
}
// Deactivate packages in current context
activeContextName, _ := p.ActiveContext()
for _, pkg := range p.InstalledPackages() {
if err := pkg.Package.deactivate(p.config, activeContextName); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to deactivate package: %s", err),
)
}
}
p.state.ActiveContext = name
if err := p.state.Save(); err != nil {
return err
}
// Update templating values
p.initTemplate()
// Activate packages in new context
for _, pkg := range p.InstalledPackages() {
if err := pkg.Package.activate(p.config, name); err != nil {
p.config.Logger.Warn(
fmt.Sprintf("failed to activate package: %s", err),
)
}
}
return nil
}

Expand Down