blob: 858221b692bb421cd5261a752c11749dfe9f3e53 [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.
// The following enables go generate to generate the doc.go file.
//go:generate go run $JIRI_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go .
package main
import (
"fmt"
"sort"
"time"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/naming"
"v.io/x/lib/cmdline"
"v.io/x/lib/set"
"v.io/x/lib/vlog"
"v.io/x/ref/lib/v23cmd"
_ "v.io/x/ref/runtime/factories/generic"
"v.io/x/ref/services/internal/binarylib"
"v.io/x/ref/services/internal/profiles"
"v.io/x/ref/services/repository"
)
var cmdBinaryTidy = &cmdline.Command{
Runner: v23cmd.RunnerFunc(runBinaryTidy),
Name: "binary",
Short: "Binary sub-command tidies a specified binaryd",
Long: `
Binary sub-command removes all binaries from a specified binaryd that
are not referenced by an applicationd envelope stored in the specified
applicationd.
`,
ArgsName: "<applicationd> <binaryd>",
ArgsLong: `
<applicationd> is the name or endpoint of the applicationd instance
sourcing the envelopes.
<binaryd> is the name or endpoint of a binaryd instance to clean up.
`,
}
// simpleGlob globs the provided endpoint as the namespace cmd does.
func mapGlob(ctx *context.T, pattern string, mapFunc func(string)) (error, []error) {
ctx, cancel := context.WithTimeout(ctx, time.Minute)
defer cancel()
ns := v23.GetNamespace(ctx)
c, err := ns.Glob(ctx, pattern)
if err != nil {
vlog.Infof("ns.Glob(%q) failed: %v", pattern, err)
return err, nil
}
errors := []*naming.GlobError{}
for res := range c {
switch v := res.(type) {
case *naming.GlobReplyEntry:
if v.Value.Name != "" {
mapFunc(v.Value.Name)
}
case *naming.GlobReplyError:
errors = append(errors, &v.Value)
}
}
globErrors := make([]error, 0, len(errors))
for _, err := range errors {
globErrors = append(globErrors, fmt.Errorf("Glob error: %s: %v\n", err.Name, err.Error))
}
return nil, globErrors
}
func logGlobErrors(env *cmdline.Env, errors []error) {
for _, err := range errors {
vlog.Errorf("Glob error: %v", err)
}
}
// getProfileNames uses glob to extract the list of profile names
// available from the binary server specified by endpoint.
func getProfileNames(ctx *context.T, env *cmdline.Env, endpoint string) ([]string, error) {
profiles, err := profiles.GetKnownProfiles()
if err != nil {
return nil, err
}
pnames := make([]string, 0, len(profiles))
for _, p := range profiles {
pnames = append(pnames, p.Label)
}
return pnames, nil
}
func getNames(ctx *context.T, env *cmdline.Env, endpoint string) ([]string, error) {
resultSet := make(map[string]struct{})
err, errors := mapGlob(ctx, endpoint, func(s string) {
resultSet[s] = struct{}{}
})
if err != nil {
return nil, err
}
logGlobErrors(env, errors)
s := set.String.ToSlice(resultSet)
sort.Strings(s)
return s, nil
}
func runBinaryTidy(ctx *context.T, env *cmdline.Env, args []string) error {
if expected, got := 2, len(args); expected != got {
return env.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
}
appEndpoint, binEndpoint := args[0], args[1]
profileNames, err := getProfileNames(ctx, env, binEndpoint)
if err != nil {
return err
}
envelopeNames, err := getNames(ctx, env, naming.Join(appEndpoint, "..."))
if err != nil {
return err
}
// Add every path in use to a set. Deletion scope is limited to
// only the binEndpoint.
bpaths := make(map[string]struct{})
for _, en := range envelopeNames {
// convert an envelope name into an envelope.
ac := repository.ApplicationClient(en)
for _, p := range profileNames {
e, err := ac.Match(ctx, []string{p})
if err != nil {
// This error case is very noisy.
vlog.VI(2).Infof("applicationd.Match(%s, %s) failed: %v\n", en, p, err)
continue
}
root, relative := naming.SplitAddressName(e.Binary.File)
if root == binEndpoint || root == "" {
bpaths[relative] = struct{}{}
}
for _, sf := range e.Packages {
root, relative := naming.SplitAddressName(sf.File)
if root == binEndpoint || root == "" {
bpaths[relative] = struct{}{}
}
}
}
}
binaryNames, err := getNames(ctx, env, naming.Join(binEndpoint, "..."))
if err != nil {
return err
}
deletionCandidates := make([]int, 0, len(binaryNames)-len(envelopeNames))
for i, bn := range binaryNames {
_, relative := naming.SplitAddressName(bn)
if _, ok := bpaths[relative]; ok {
// relative is mentioned in an envelope.
continue
}
if _, err := binarylib.Stat(ctx, bn); err != nil {
// This name is not a binary.
continue
}
deletionCandidates = append(deletionCandidates, i)
}
for _, i := range deletionCandidates {
b := binaryNames[i]
if err := binarylib.Delete(ctx, b); err != nil {
vlog.Errorf("Couldn't delete binary %s: %v", b, err)
}
}
return nil
}
var cmdApplicationTidy = &cmdline.Command{
Runner: v23cmd.RunnerFunc(runApplicationTidy),
Name: "application",
Short: "Application sub-command tidies a specified applicationd",
Long: `
Application sub-command uses the Tidy RPC to clean up outdated
envelopes from the specified appilcationd. Call this before tidying a
binaryd instance for maximum tidiness.
`,
ArgsName: "<applicationd>",
ArgsLong: `
<applicationd> is the name or endpoint of the applicationd instance
to tidy.
`,
}
func runApplicationTidy(ctx *context.T, env *cmdline.Env, args []string) error {
vlog.Infof("runApplicationTidy")
if expected, got := 1, len(args); expected != got {
return env.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
}
ac := repository.ApplicationClient(args[0])
return ac.TidyNow(ctx)
}
var cmdRoot = &cmdline.Command{
Name: "tidy",
Short: "Tidy binary repositories",
Long: `
Tidy tidies the Vanadium repository by removing unused
envelopes and binaries.
`,
Children: []*cmdline.Command{cmdBinaryTidy, cmdApplicationTidy},
}
func main() {
cmdline.HideGlobalFlagsExcept()
cmdline.Main(cmdRoot)
}