Skip to content

Commit

Permalink
pack: separate registry list and pack list commands (#337)
Browse files Browse the repository at this point in the history
This PR changes `nomad-pack registry list` command to display a list of
registries and adds a new `nomad-pack list` command that lists only packs.
  • Loading branch information
pkazmierczak committed Jun 6, 2023
1 parent c7509bb commit dc942fd
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 90 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ IMPROVEMENTS:

* cli: Add `generate var-file` command [[GH-333](https://github.com/hashicorp/nomad-pack/pull/333)]
* cli: `registry list` command now shows git refs to repositories present in the cache [[GH-318](https://github.com/hashicorp/nomad-pack/pull/318)]
* cli: `registry list` command now shows only registries, and a new command `list` shows packs [[GH-337](https://github.com/hashicorp/nomad-pack/pull/337)]
* deps: Update the Nomad OpenAPI dependency; require Go 1.18 as a build dependency [[GH-288](https://github.com/hashicorp/nomad-pack/pull/288)]
* pack: Author field no longer supported in pack metadata [[GH-317](https://github.com/hashicorp/nomad-pack/pull/317)]
* pack: URL field no longer supported in pack metadata [[GH-343](https://github.com/hashicorp/nomad-pack/pull/343)]
Expand Down
36 changes: 21 additions & 15 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ const (
func TestCLI_CreateTestRegistry(t *testing.T) {
// This test is here to help setup the pack registry cache. It needs to be
// the first one in the file and can not be `Parallel()`
regName, regPath := createTestRegistry(t)
reg, regPath := createTestRegistry(t)
defer cleanTestRegistry(t, regPath)
t.Logf("regName: %v\n", regName)
t.Logf("regName: %v\n", reg.Name)
t.Logf("regPath: %v\n", regPath)
err := filepath.Walk(regPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
Expand All @@ -65,12 +65,12 @@ func TestCLI_CreateTestRegistry(t *testing.T) {

result := runPackCmd(t, []string{"registry", "list"})
out := result.cmdOut.String()
packRegex := regexp.MustCompile(`(?m)^ +` + testPack + ` +\| (\w+) +\| (\w+) +\| \d\.\d\.\d +\| ` + regName + ` +\|[^\n]+?$`)
matches := packRegex.FindAllString(out, -1)
regex := regexp.MustCompile(`(?m)^ +` + reg.Name + ` +\| (\w+) +\| (\w+) +\| ` + reg.Source + ` +[^\n]+?$`)
matches := regex.FindAllString(out, -1)
for i, match := range matches {
t.Logf("match %v: %v\n", i, match)
}
must.RegexMatch(t, packRegex, out)
must.RegexMatch(t, regex, out)
must.Eq(t, 0, result.exitCode)
}

Expand Down Expand Up @@ -162,10 +162,10 @@ func TestCLI_JobPlan_BadJob(t *testing.T) {
// Confirm that another pack with the same job names but a different deployment name fails
func TestCLI_JobPlan_ConflictingDeployment(t *testing.T) {
ct.HTTPTestParallel(t, ct.WithDefaultConfig(), func(s *agent.TestAgent) {
regName, regPath := createTestRegistry(t)
reg, regPath := createTestRegistry(t)
defer cleanTestRegistry(t, regPath)

testRegFlag := "--registry=" + regName
testRegFlag := "--registry=" + reg.Name
expectGoodPackDeploy(t, runTestPackCmd(t, s, []string{"run", testPack, testRegFlag}))

result := runTestPackCmd(t, s, []string{"run", testPack, testRegFlag, testRefFlag})
Expand Down Expand Up @@ -373,7 +373,7 @@ func TestCLI_PackDestroy_WithOverrides(t *testing.T) {
c, err := ct.NewTestClient(s)
must.NoError(t, err)
// Because this test uses ref, it requires a populated pack cache.
regName, regPath := createTestRegistry(t)
reg, regPath := createTestRegistry(t)
defer cleanTestRegistry(t, regPath)

// TODO: Table Testing
Expand All @@ -388,17 +388,17 @@ func TestCLI_PackDestroy_WithOverrides(t *testing.T) {
"run",
testPack,
"--var=job_name=" + j,
"--registry=" + regName,
"--registry=" + reg.Name,
}))
}

// Stop nonexistent job
result := runTestPackCmd(t, s, []string{"destroy", testPack, "--var=job_name=baz", "--registry=" + regName})
result := runTestPackCmd(t, s, []string{"destroy", testPack, "--var=job_name=baz", "--registry=" + reg.Name})
must.Eq(t, 1, result.exitCode, must.Sprintf(
"expected exitcode 1; got %v\ncmdOut:%v", result.exitCode, result.cmdOut.String()))

// Stop job with var override
result = runTestPackCmd(t, s, []string{"destroy", testPack, "--var=job_name=foo", "--registry=" + regName})
result = runTestPackCmd(t, s, []string{"destroy", testPack, "--var=job_name=foo", "--registry=" + reg.Name})
must.Eq(t, 0, result.exitCode, must.Sprintf(
"expected exitcode 0; got %v\ncmdOut:%v", result.exitCode, result.cmdOut.String()))

Expand All @@ -408,7 +408,7 @@ func TestCLI_PackDestroy_WithOverrides(t *testing.T) {
must.NotNil(t, job)

// Stop job with no overrides passed
result = runTestPackCmd(t, s, []string{"destroy", testPack, "--registry=" + regName})
result = runTestPackCmd(t, s, []string{"destroy", testPack, "--registry=" + reg.Name})
must.Eq(t, 0, result.exitCode, must.Sprintf(
"expected exitcode 0; got %v\ncmdOut:%v", result.exitCode, result.cmdOut.String()))

Expand Down Expand Up @@ -846,7 +846,7 @@ func expectGoodPackPlan(t *testing.T, r PackCommandResult) {
must.Eq(t, 1, r.exitCode) // exitcode 1 means that an allocation will be created
}

func createTestRegistry(t *testing.T) (string, string) {
func createTestRegistry(t *testing.T) (*cache.Registry, string) {
// Fake a clone
registryName := fmt.Sprintf("test-%v", time.Now().UnixMilli())
regDir := path.Join(cache.DefaultCachePath(), registryName)
Expand All @@ -860,10 +860,16 @@ func createTestRegistry(t *testing.T) (string, string) {

// Put a sample metadata.json in the test registry
metaPath := filepath.Join(regDir, "metadata.json")
b, _ := json.Marshal(&cache.Registry{Name: registryName, Source: "", Ref: testRef, LocalRef: testRef})
registry := &cache.Registry{
Name: registryName,
Source: "github.com/hashicorp/nomad-pack-test-registry",
Ref: testRef,
LocalRef: testRef,
}
b, _ := json.Marshal(registry)
os.WriteFile(metaPath, b, 0644)

return path.Base(regDir), regDir
return registry, regDir
}

func cleanTestRegistry(t *testing.T, regPath string) {
Expand Down
28 changes: 6 additions & 22 deletions internal/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@ import (
"errors"
"fmt"
"io"
"os"
"path"
"runtime"

"github.com/hashicorp/go-hclog"
"github.com/hashicorp/nomad-pack/internal/pkg/cache"
flag "github.com/hashicorp/nomad-pack/internal/pkg/flag"
"github.com/hashicorp/nomad-pack/internal/pkg/variable"
"github.com/hashicorp/nomad-pack/terminal"
"github.com/hashicorp/nomad/api"
"github.com/mitchellh/go-wordwrap"
"github.com/posener/complete"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/hashicorp/nomad-pack/internal/pkg/cache"
flag "github.com/hashicorp/nomad-pack/internal/pkg/flag"
"github.com/hashicorp/nomad-pack/internal/pkg/variable"
"github.com/hashicorp/nomad-pack/terminal"
)

// baseCommand is embedded in all commands to provide common logic and data.
Expand Down Expand Up @@ -215,29 +214,14 @@ func (c *baseCommand) Init(opts ...Option) error {

func (c *baseCommand) ensureCache() error {
// Creates global cache
globalCache, err := cache.NewCache(&cache.CacheConfig{
_, err := cache.NewCache(&cache.CacheConfig{
Path: cache.DefaultCachePath(),
Logger: c.ui,
})
if err != nil {
return err
}

// Check if default registry exists
_, err = os.Stat(path.Join(cache.DefaultCachePath(), cache.DefaultRegistryName))
// If it does not error, then the registry already exists
if err == nil {
return nil
}

// Add the registry or registry target to the global cache
_, err = globalCache.Add(&cache.AddOpts{
RegistryName: cache.DefaultRegistryName,
Source: cache.DefaultRegistrySource,
})
if err != nil {
return err
}
return nil
}

Expand Down
51 changes: 32 additions & 19 deletions internal/cli/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,31 @@ func generatePackManager(c *baseCommand, client *api.Client, packCfg *cache.Pack
}

func registryTable() *terminal.Table {
return terminal.NewTable("PACK NAME", "REF", "LOCAL_REF", "METADATA VERSION", "REGISTRY", "REGISTRY_URL")
return terminal.NewTable("REGISTRY NAME", "REF", "LOCAL_REF", "REGISTRY_URL")
}

func emptyRegistryTableRow(cachedRegistry *cache.Registry) []terminal.TableEntry {
func registryPackTable() *terminal.Table {
return terminal.NewTable("PACK NAME", "REF", "LOCAL_REF", "METADATA VERSION", "REGISTRY NAME", "REGISTRY_URL")
}

func packTable() *terminal.Table {
return terminal.NewTable("PACK NAME", "METADATA VERSION", "REGISTRY NAME")
}

func registryTableRow(cachedRegistry *cache.Registry) []terminal.TableEntry {
return []terminal.TableEntry{
// blank pack name
{
Value: "",
},
// blank revision
{
Value: "",
},
// blank local ref
{
Value: "",
Value: cachedRegistry.Name,
},
// blank metadata version
{
Value: "",
Value: cachedRegistry.Ref,
},
// CachedRegistry name - user defined alias or registry URL slug
{
Value: cachedRegistry.Name,
Value: cachedRegistry.LocalRef,
},
// The cachedRegistry URL from where the registryPack was cloned
{
Value: cachedRegistry.Source,
},
// TODO: The app version
}
}

Expand All @@ -99,7 +94,7 @@ func registryPackRow(cachedRegistry *cache.Registry, cachedPack *cache.Pack) []t
{
Value: cachedPack.Metadata.Pack.Version,
},
// CachedRegistry name - user defined alias or registry URL slug
// CachedRegistry name user defined alias or registry URL slug
{
Value: cachedRegistry.Name,
},
Expand All @@ -111,6 +106,24 @@ func registryPackRow(cachedRegistry *cache.Registry, cachedPack *cache.Pack) []t
}
}

func packRow(cachedRegistry *cache.Registry, cachedPack *cache.Pack) []terminal.TableEntry {
return []terminal.TableEntry{
// The Name of the registryPack
{
Value: cachedPack.Name(),
},
// The metadata version
{
Value: cachedPack.Metadata.Pack.Version,
},
// CachedRegistry name user defined alias or registry URL slug
{
Value: fmt.Sprintf("%s@%s", cachedRegistry.Name, cachedRegistry.LocalRef),
},
// TODO: The app version
}
}

// TODO: This needs to be on a domain specific pkg rather than a UI helpers file.
// This will be possible once we create a logger interface that can be passed
// between layers.
Expand Down
92 changes: 92 additions & 0 deletions internal/cli/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package cli

import (
"github.com/hashicorp/nomad-pack/internal/pkg/cache"
"github.com/hashicorp/nomad-pack/internal/pkg/flag"
"github.com/posener/complete"
)

// ListCommand lists all registries and pack that have been downloaded
// to the current machine.
type ListCommand struct {
*baseCommand
}

func (c *ListCommand) Run(args []string) int {
c.cmdKey = "registry list"
// Initialize. If we fail, we just exit since Init handles the UI.
if err := c.Init(
WithNoArgs(args),
WithNoConfig(),
WithClient(false),
); err != nil {
return 1
}

// Get the global cache dir - may be configurable in the future, so using this
// helper function rather than a direct reference to the CONST.
globalCache, err := cache.NewCache(&cache.CacheConfig{
Path: cache.DefaultCachePath(),
Logger: c.ui,
})
if err != nil {
return 1
}

// Load the list of registries.
err = globalCache.Load()
if err != nil {
return 1
}

// Iterate over the registries and build a table row for each cachedRegistry/pack
// entry at each ref. Hierarchically, this should equate to the default
// cachedRegistry and all its peers.
if len(globalCache.Registries()) > 0 {
table := packTable()
for _, cachedRegistry := range globalCache.Registries() {
for _, registryPack := range cachedRegistry.Packs {
tableRow := packRow(cachedRegistry, registryPack)
table.Rows = append(table.Rows, tableRow)
}
}
// Display output table
c.ui.Table(table)
} else {
c.ui.Output("No packs present in the cache.")
}

return 0
}

func (c *ListCommand) Flags() *flag.Sets {
return c.flagSet(0, nil)
}

func (c *ListCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictNothing
}

func (c *ListCommand) AutocompleteFlags() complete.Flags {
return c.Flags().Completions()
}

func (c *ListCommand) Synopsis() string {
return "List packs available in the local environment."
}

func (c *ListCommand) Help() string {
c.Example = `
# List all available packs
nomad-pack list
`
return formatHelp(`
Usage: nomad-pack list
List nomad packs.
` + c.GetExample() + c.Flags().Help())
}
5 changes: 5 additions & 0 deletions internal/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ func Commands(
baseCommand: baseCommand,
}, nil
},
"list": func() (cli.Command, error) {
return &ListCommand{
baseCommand: baseCommand,
}, nil
},
"stop": func() (cli.Command, error) {
return &StopCommand{
baseCommand: baseCommand,
Expand Down
Loading

0 comments on commit dc942fd

Please sign in to comment.