blob: 452a0959f6473ac55cb7723c0944ec82f57f4488 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package profilescmdline
import (
"bufio"
"bytes"
"fmt"
"sort"
"strings"
"v.io/jiri"
"v.io/jiri/profiles"
"v.io/jiri/profiles/profilesmanager"
"v.io/jiri/profiles/profilesutil"
"v.io/jiri/runutil"
)
// profileManager is implemented for both in-process and sub-command
// implemented profiles.
type profileManager interface {
packageCmds(jirix *jiri.X, cl *packagesFlagValues, root jiri.RelPath) ([][]string, error)
install(jirix *jiri.X, cl *installFlagValues, root jiri.RelPath) error
uninstall(jirix *jiri.X, cl *uninstallFlagValues, root jiri.RelPath) error
update(jirix *jiri.X, cl *updateFlagValues, root jiri.RelPath) error
cleanup(jirix *jiri.X, cl *cleanupFlagValues, root jiri.RelPath) error
mgrName() string
}
func newProfileManager(name string, db *profiles.DB) profileManager {
installer, profile := profiles.SplitProfileName(name)
installer = strings.TrimSpace(installer)
if len(installer) == 0 || installer == profileInstaller {
return &inproc{installer, profile, name, db}
}
return &subcommand{installer, profile, name, db}
}
type inproc struct {
installer, name, qname string
db *profiles.DB
}
func (ip *inproc) mgrName() string {
return ip.qname
}
func (ip *inproc) packageCmds(jirix *jiri.X, cl *packagesFlagValues, root jiri.RelPath) ([][]string, error) {
mgr := profilesmanager.LookupManager(ip.qname)
if mgr == nil {
return nil, fmt.Errorf("profile %v is not available via this installer %q", ip.qname, ip.installer)
}
def, err := targetAtDefaultVersion(mgr, cl.target)
if err != nil {
return nil, err
}
needed, err := mgr.OSPackages(jirix, ip.db, root, def)
if err != nil {
return nil, fmt.Errorf("Failed to obtain packages for %v %v: %v", ip.name, cl.target, err)
}
if !cl.allPackages {
if missing, err := profilesutil.MissingOSPackages(jirix, needed); err != nil {
return nil, fmt.Errorf("Failed to obtain missing packages for %v %v: %v", ip.name, cl.target, err)
} else {
needed = missing
}
}
// Dedup & sort.
deduped := []string{}
m := map[string]bool{}
for _, n := range needed {
if !m[n] {
m[n] = true
deduped = append(deduped, n)
}
}
sort.Strings(deduped)
return profilesutil.OSPackageInstallCommands(jirix, deduped), nil
}
func (ip *inproc) install(jirix *jiri.X, cl *installFlagValues, root jiri.RelPath) error {
mgr := profilesmanager.LookupManager(ip.qname)
if mgr == nil {
return fmt.Errorf("profile %v is not available via this installer %q", ip.qname, ip.installer)
}
def, err := targetAtDefaultVersion(mgr, cl.target)
if err != nil {
return err
}
err = mgr.Install(jirix, ip.db, root, def)
logResult(jirix, "Install", mgr, def, err)
return err
}
func (ip *inproc) uninstall(jirix *jiri.X, cl *uninstallFlagValues, root jiri.RelPath) error {
profile := ip.db.LookupProfile(ip.installer, ip.name)
if profile == nil {
fmt.Fprintf(jirix.Stdout(), "%s is not installed\n", ip.qname)
return nil
}
mgr := profilesmanager.LookupManager(ip.qname)
var targets []*profiles.Target
if cl.allTargets {
targets = profile.Targets()
} else {
def, err := targetAtDefaultVersion(mgr, cl.target)
if err != nil {
return err
}
targets = []*profiles.Target{&def}
}
for _, target := range targets {
if err := mgr.Uninstall(jirix, ip.db, root, *target); err != nil {
logResult(jirix, "Uninstall", mgr, *target, err)
return err
}
logResult(jirix, "Uninstall", mgr, *target, nil)
}
return nil
}
func (ip *inproc) update(jirix *jiri.X, cl *updateFlagValues, root jiri.RelPath) error {
profile := ip.db.LookupProfile(ip.installer, ip.name)
if profile == nil {
// silently ignore uninstalled profile.
return nil
}
mgr := profilesmanager.LookupManager(ip.qname)
vi := mgr.VersionInfo()
for _, target := range profile.Targets() {
if vi.IsTargetOlderThanDefault(target.Version()) {
// Check if default target is already installed.
defTarget := *target
defTarget.SetVersion(vi.Default())
if profiles.FindTarget(profile.Targets(), &defTarget) != nil {
// Default target is already installed. Skip.
continue
}
if cl.verbose {
fmt.Fprintf(jirix.Stdout(), "Updating %s %s from %q to %s\n", ip.qname, target, target.Version(), vi)
}
err := mgr.Install(jirix, ip.db, root, defTarget)
logResult(jirix, "Update", mgr, defTarget, err)
if err != nil {
return err
}
} else {
if cl.verbose {
fmt.Fprintf(jirix.Stdout(), "%s %s at %q is up to date(%s)\n", ip.qname, target, target.Version(), vi)
}
}
}
return nil
}
func cleanupGC(jirix *jiri.X, db *profiles.DB, root jiri.RelPath, verbose bool, name string) error {
mgr := profilesmanager.LookupManager(name)
if mgr == nil {
fmt.Fprintf(jirix.Stderr(), "%s is not linked into this binary\n", name)
return nil
}
vi := mgr.VersionInfo()
installer, profileName := profiles.SplitProfileName(name)
profile := db.LookupProfile(installer, profileName)
for _, target := range profile.Targets() {
if vi.IsTargetOlderThanDefault(target.Version()) {
err := mgr.Uninstall(jirix, db, root, *target)
logResult(jirix, "Cleanup: -gc", mgr, *target, err)
if err != nil {
return err
}
}
}
return nil
}
func cleanupRmAll(jirix *jiri.X, db *profiles.DB, root jiri.RelPath) error {
s := jirix.NewSeq()
if err := s.AssertFileExists(db.Path()).Remove(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) {
return err
} else {
if err := s.AssertDirExists(db.Path()).RemoveAll(db.Path()).Done(); err != nil && !runutil.IsNotExist(err) {
return err
}
}
d := root.Abs(jirix)
err := s.AssertDirExists(d).
Run("chmod", "-R", "u+w", d).
RemoveAll(d).
Done()
if err == nil || runutil.IsNotExist(err) {
fmt.Fprintf(jirix.Stdout(), "success\n")
return nil
} else {
fmt.Fprintf(jirix.Stdout(), "%v\n", err)
}
return err
}
func (ip *inproc) cleanup(jirix *jiri.X, cl *cleanupFlagValues, root jiri.RelPath) error {
if cl.gc {
if cl.verbose {
fmt.Fprintf(jirix.Stdout(), "Removing targets older than the default version for %s\n", ip.qname)
}
if err := cleanupGC(jirix, ip.db, root, cl.verbose, ip.qname); err != nil {
return fmt.Errorf("gc: %v", err)
}
}
if cl.rmAll {
if cl.verbose {
fmt.Fprintf(jirix.Stdout(), "Removing profile manifest and all profile output files\n")
}
if err := cleanupRmAll(jirix, ip.db, root); err != nil {
return err
}
}
return nil
}
type subcommand struct {
installer, profile, qname string
db *profiles.DB
}
func (sc *subcommand) mgrName() string {
return sc.qname
}
func (sc *subcommand) run(jirix *jiri.X, verb string, args []string) error {
cl := []string{"profile-" + sc.installer, verb}
cl = append(cl, args...)
cl = append(cl, sc.qname)
return jirix.NewSeq().Capture(jirix.Stdout(), jirix.Stderr()).Last("jiri", cl...)
}
func (sc *subcommand) packageCmds(jirix *jiri.X, cl *packagesFlagValues, root jiri.RelPath) ([][]string, error) {
cmd := []string{"profile-" + sc.installer, "os-packages"}
cmd = append(cmd, cl.args()...)
cmd = append(cmd, sc.qname)
var out bytes.Buffer
if err := jirix.NewSeq().Capture(&out, jirix.Stderr()).Last("jiri", cmd...); err != nil {
return nil, err
}
scanner := bufio.NewScanner(&out)
cmds := make([][]string, 0, 5)
for scanner.Scan() {
cmds = append(cmds, strings.Split(scanner.Text(), " "))
}
return cmds, nil
}
func (sc *subcommand) install(jirix *jiri.X, cl *installFlagValues, root jiri.RelPath) error {
return sc.run(jirix, "install", cl.args())
}
func (sc *subcommand) uninstall(jirix *jiri.X, cl *uninstallFlagValues, root jiri.RelPath) error {
return sc.run(jirix, "uninstall", cl.args())
}
func (sc *subcommand) update(jirix *jiri.X, cl *updateFlagValues, root jiri.RelPath) error {
return sc.run(jirix, "update", cl.args())
}
func (sc *subcommand) cleanup(jirix *jiri.X, cl *cleanupFlagValues, root jiri.RelPath) error {
return sc.run(jirix, "cleanup", cl.args())
}
func logResult(jirix *jiri.X, action string, mgr profiles.Manager, target profiles.Target, err error) {
fmt.Fprintf(jirix.Stdout(), "%s: %s %s: ", action, profiles.QualifiedProfileName(mgr.Installer(), mgr.Name()), target)
if err == nil {
fmt.Fprintf(jirix.Stdout(), "success\n")
} else {
fmt.Fprintf(jirix.Stdout(), "%v\n", err)
}
}