mirror of
https://github.com/containers/podman.git
synced 2025-12-11 09:18:34 +08:00
image lookup: do not match *any* tags
For reasons buried in the history of Podman, looking up an untagged image would match any tag of matching image. For instance, looking up centos would match a local image centos:foobar. Change that behavior to only match the latest tag. Fix: #11964 Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
49
vendor/github.com/containers/common/pkg/report/doc.go
generated
vendored
49
vendor/github.com/containers/common/pkg/report/doc.go
generated
vendored
@@ -3,34 +3,44 @@ Package report provides helper structs/methods/funcs for formatting output
|
||||
|
||||
To format output for an array of structs:
|
||||
|
||||
w := report.NewWriterDefault(os.Stdout)
|
||||
defer w.Flush()
|
||||
|
||||
ExamplePodman:
|
||||
headers := report.Headers(struct {
|
||||
ID string
|
||||
}{}, nil)
|
||||
t, _ := report.NewTemplate("command name").Parse("{{range .}}{{.ID}}{{end}}")
|
||||
t.Execute(t, headers)
|
||||
t.Execute(t, map[string]string{
|
||||
|
||||
f := report.New(os.Stdout, "Command Name")
|
||||
f, _ := f.Parse(report.OriginPodman, "{{range .}}{{.ID}}{{end}}")
|
||||
defer f.Flush()
|
||||
|
||||
if f.RenderHeaders {
|
||||
f.Execute(headers)
|
||||
}
|
||||
f.Execute( map[string]string{
|
||||
"ID":"fa85da03b40141899f3af3de6d27852b",
|
||||
})
|
||||
// t.IsTable() == false
|
||||
|
||||
or
|
||||
|
||||
w := report.NewWriterDefault(os.Stdout)
|
||||
defer w.Flush()
|
||||
// Output:
|
||||
// ID
|
||||
// fa85da03b40141899f3af3de6d27852b
|
||||
|
||||
ExampleUser:
|
||||
headers := report.Headers(struct {
|
||||
CID string
|
||||
}{}, map[string]string{
|
||||
"CID":"ID"})
|
||||
t, _ := report.NewTemplate("command name").Parse("table {{.CID}}")
|
||||
t.Execute(t, headers)
|
||||
}{}, map[string]string{"CID":"ID"})
|
||||
|
||||
f, _ := report.New(os.Stdout, "Command Name").Parse(report.OriginUser, "table {{.CID}}")
|
||||
defer f.Flush()
|
||||
|
||||
if f.RenderHeaders {
|
||||
t.Execute(t, headers)
|
||||
}
|
||||
t.Execute(t,map[string]string{
|
||||
"CID":"fa85da03b40141899f3af3de6d27852b",
|
||||
})
|
||||
// t.IsTable() == true
|
||||
|
||||
// Output:
|
||||
// ID
|
||||
// fa85da03b40141899f3af3de6d27852b
|
||||
|
||||
Helpers:
|
||||
|
||||
@@ -38,13 +48,20 @@ Helpers:
|
||||
... process JSON and output
|
||||
}
|
||||
|
||||
if report.HasTable(cmd.Flag("format").Value.String()) {
|
||||
... "table" keyword prefix in format text
|
||||
}
|
||||
|
||||
Template Functions:
|
||||
|
||||
The following template functions are added to the template when parsed:
|
||||
- join strings.Join, {{join .Field separator}}
|
||||
- json encode field as JSON {{ json .Field }}
|
||||
- lower strings.ToLower {{ .Field | lower }}
|
||||
- pad add spaces as prefix and suffix {{ pad . 2 2 }}
|
||||
- split strings.Split {{ .Field | split }}
|
||||
- title strings.Title {{ .Field | title }}
|
||||
- truncate limit field length {{ truncate . 10 }}
|
||||
- upper strings.ToUpper {{ .Field | upper }}
|
||||
|
||||
report.Funcs() may be used to add additional template functions.
|
||||
|
||||
151
vendor/github.com/containers/common/pkg/report/formatter.go
generated
vendored
Normal file
151
vendor/github.com/containers/common/pkg/report/formatter.go
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// Flusher is the interface that wraps the Flush method.
|
||||
type Flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// NopFlusher represents a type which flush operation is nop.
|
||||
type NopFlusher struct{}
|
||||
|
||||
// Flush is a nop operation.
|
||||
func (f *NopFlusher) Flush() (err error) { return }
|
||||
|
||||
type Origin int
|
||||
|
||||
const (
|
||||
OriginUnknown Origin = iota
|
||||
OriginPodman
|
||||
OriginUser
|
||||
)
|
||||
|
||||
func (o Origin) String() string {
|
||||
switch o {
|
||||
case OriginPodman:
|
||||
return "OriginPodman"
|
||||
case OriginUser:
|
||||
return "OriginUser"
|
||||
default:
|
||||
return "OriginUnknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Formatter holds the configured Writer and parsed Template, additional state fields are
|
||||
// maintained to assist in the podman command report writing.
|
||||
type Formatter struct {
|
||||
Origin Origin // Source of go template. OriginUser or OriginPodman
|
||||
RenderHeaders bool // Hint, default behavior for given template is to include headers
|
||||
RenderTable bool // Does template have "table" keyword
|
||||
flusher Flusher // Flush any buffered formatted output
|
||||
template *template.Template // Go text/template for formatting output
|
||||
text string // value of canonical template after processing
|
||||
writer io.Writer // Destination for formatted output
|
||||
}
|
||||
|
||||
// Parse parses golang template returning a formatter
|
||||
//
|
||||
// - OriginPodman implies text is a template from podman code. Output will
|
||||
// be filtered through a tabwriter.
|
||||
//
|
||||
// - OriginUser implies text is a template from a user. If template includes
|
||||
// keyword "table" output will be filtered through a tabwriter.
|
||||
func (f *Formatter) Parse(origin Origin, text string) (*Formatter, error) {
|
||||
f.Origin = origin
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(text, "table "):
|
||||
f.RenderTable = true
|
||||
text = "{{range .}}" + NormalizeFormat(text) + "{{end -}}"
|
||||
case OriginUser == origin:
|
||||
text = EnforceRange(NormalizeFormat(text))
|
||||
default:
|
||||
text = NormalizeFormat(text)
|
||||
}
|
||||
f.text = text
|
||||
|
||||
if f.RenderTable || origin == OriginPodman {
|
||||
tw := tabwriter.NewWriter(f.writer, 12, 2, 2, ' ', tabwriter.StripEscape)
|
||||
f.writer = tw
|
||||
f.flusher = tw
|
||||
f.RenderHeaders = true
|
||||
}
|
||||
|
||||
tmpl, err := f.template.Funcs(template.FuncMap(DefaultFuncs)).Parse(text)
|
||||
if err != nil {
|
||||
return f, err
|
||||
}
|
||||
f.template = tmpl
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Funcs adds the elements of the argument map to the template's function map.
|
||||
// A default template function will be replaced if there is a key collision.
|
||||
func (f *Formatter) Funcs(funcMap template.FuncMap) *Formatter {
|
||||
m := make(template.FuncMap, len(DefaultFuncs)+len(funcMap))
|
||||
for k, v := range DefaultFuncs {
|
||||
m[k] = v
|
||||
}
|
||||
for k, v := range funcMap {
|
||||
m[k] = v
|
||||
}
|
||||
f.template = f.template.Funcs(funcMap)
|
||||
return f
|
||||
}
|
||||
|
||||
// Init either resets the given tabwriter with new values or wraps w in tabwriter with given values
|
||||
func (f *Formatter) Init(w io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Formatter {
|
||||
flags |= tabwriter.StripEscape
|
||||
|
||||
if tw, ok := f.writer.(*tabwriter.Writer); ok {
|
||||
tw = tw.Init(w, minwidth, tabwidth, padding, padchar, flags)
|
||||
f.writer = tw
|
||||
f.flusher = tw
|
||||
} else {
|
||||
tw = tabwriter.NewWriter(w, minwidth, tabwidth, padding, padchar, flags)
|
||||
f.writer = tw
|
||||
f.flusher = tw
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Execute applies a parsed template to the specified data object,
|
||||
// and writes the output to Formatter.Writer.
|
||||
func (f *Formatter) Execute(data interface{}) error {
|
||||
return f.template.Execute(f.writer, data)
|
||||
}
|
||||
|
||||
// Flush should be called after the last call to Write to ensure
|
||||
// that any data buffered in the Formatter is written to output. Any
|
||||
// incomplete escape sequence at the end is considered
|
||||
// complete for formatting purposes.
|
||||
func (f *Formatter) Flush() error {
|
||||
// Indirection is required here to prevent caller from having to know when
|
||||
// value of Flusher may be changed.
|
||||
return f.flusher.Flush()
|
||||
}
|
||||
|
||||
// Writer returns the embedded io.Writer from Formatter
|
||||
func (f *Formatter) Writer() io.Writer {
|
||||
return f.writer
|
||||
}
|
||||
|
||||
// New allocates a new, undefined Formatter with the given name and Writer
|
||||
func New(output io.Writer, name string) *Formatter {
|
||||
f := new(Formatter)
|
||||
|
||||
f.flusher = new(NopFlusher)
|
||||
if flusher, ok := output.(Flusher); ok {
|
||||
f.flusher = flusher
|
||||
}
|
||||
|
||||
f.template = template.New(name)
|
||||
f.writer = output
|
||||
return f
|
||||
}
|
||||
6
vendor/github.com/containers/common/pkg/report/template.go
generated
vendored
6
vendor/github.com/containers/common/pkg/report/template.go
generated
vendored
@@ -25,17 +25,19 @@ var tableReplacer = strings.NewReplacer(
|
||||
"table ", "",
|
||||
`\t`, "\t",
|
||||
" ", "\t",
|
||||
`\n`, "\n",
|
||||
)
|
||||
|
||||
// escapedReplacer will clean up escaped characters from CLI
|
||||
var escapedReplacer = strings.NewReplacer(
|
||||
`\t`, "\t",
|
||||
`\n`, "\n",
|
||||
)
|
||||
|
||||
var DefaultFuncs = FuncMap{
|
||||
"join": strings.Join,
|
||||
"json": func(v interface{}) string {
|
||||
buf := &bytes.Buffer{}
|
||||
buf := new(bytes.Buffer)
|
||||
enc := json.NewEncoder(buf)
|
||||
enc.SetEscapeHTML(false)
|
||||
enc.Encode(v)
|
||||
@@ -157,7 +159,7 @@ func (t *Template) IsTable() bool {
|
||||
return t.isTable
|
||||
}
|
||||
|
||||
var rangeRegex = regexp.MustCompile(`{{\s*range\s*\.\s*}}.*{{\s*end\s*-?\s*}}`)
|
||||
var rangeRegex = regexp.MustCompile(`(?s){{\s*range\s*\.\s*}}.*{{\s*end\s*-?\s*}}`)
|
||||
|
||||
// EnforceRange ensures that the format string contains a range
|
||||
func EnforceRange(format string) string {
|
||||
|
||||
Reference in New Issue
Block a user