-
Notifications
You must be signed in to change notification settings - Fork 799
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
proxy: Add optional flags to OpenImage
#1844
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,8 @@ import ( | |
// 0.2.3: Added GetFullConfig | ||
// 0.2.4: Added OpenImageOptional | ||
// 0.2.5: Added LayerInfoJSON | ||
const protocolVersion = "0.2.5" | ||
// 0.2.6: OpenImage now accepts a second argument with an array of options | ||
const protocolVersion = "0.2.6" | ||
|
||
// maxMsgSize is the current limit on a packet size. | ||
// Note that all non-metadata (i.e. payload data) is sent over a pipe. | ||
|
@@ -214,8 +215,18 @@ func (h *proxyHandler) Initialize(args []interface{}) (replyBuf, error) { | |
|
||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`. | ||
// The return value is an opaque integer handle. | ||
// | ||
// A second argument may be present; if so, it is an array of string-valued flags. | ||
// | ||
// - optional: Do not error if the image is not present; instead return a zero imageid. Since: 0.2.6 | ||
func (h *proxyHandler) OpenImage(args []interface{}) (replyBuf, error) { | ||
return h.openImageImpl(args, false) | ||
opts := openOptions{} | ||
if len(args) > 1 { | ||
if err := opts.parse(args[1]); err != nil { | ||
return replyBuf{}, err | ||
} | ||
} | ||
return h.openImageImpl(args, opts) | ||
} | ||
|
||
// isDockerManifestUnknownError is a copy of code from containers/image, | ||
|
@@ -237,15 +248,45 @@ func isNotFoundImageError(err error) bool { | |
errors.Is(err, ocilayout.ImageNotFoundError{}) | ||
} | ||
|
||
func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (replyBuf, error) { | ||
type openOptions struct { | ||
optional bool | ||
} | ||
|
||
func (o *openOptions) parse(argsval interface{}) error { | ||
args, ok := argsval.([]interface{}) | ||
if !ok { | ||
return fmt.Errorf("expecting array for options, not %T", argsval) | ||
} | ||
for _, argv := range args { | ||
arg, ok := argv.(string) | ||
if !ok { | ||
return fmt.Errorf("expecting string option, not %T", arg) | ||
} | ||
switch arg { | ||
case "optional": | ||
o.optional = true | ||
default: | ||
return fmt.Errorf("unknown option %s", arg) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (h *proxyHandler) openImageImpl(args []interface{}, opts openOptions) (replyBuf, error) { | ||
h.lock.Lock() | ||
defer h.lock.Unlock() | ||
var ret replyBuf | ||
|
||
if h.sysctx == nil { | ||
return ret, fmt.Errorf("client error: must invoke Initialize") | ||
} | ||
if len(args) != 1 { | ||
switch len(args) { | ||
case 1: | ||
// This is is the default | ||
case 2: | ||
// The second argument, if present should have been parsed by the caller | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. … but I think enforcing Probably in |
||
default: | ||
return ret, fmt.Errorf("invalid request, expecting one argument") | ||
} | ||
imageref, ok := args[0].(string) | ||
|
@@ -259,7 +300,7 @@ func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (re | |
} | ||
imgsrc, err := imgRef.NewImageSource(context.Background(), h.sysctx) | ||
if err != nil { | ||
if allowNotFound && isNotFoundImageError(err) { | ||
if opts.optional && isNotFoundImageError(err) { | ||
ret.value = sentinelImageID | ||
return ret, nil | ||
} | ||
|
@@ -283,7 +324,9 @@ func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (re | |
// The return value is an opaque integer handle. If the image does not exist, zero | ||
// is returned. | ||
func (h *proxyHandler) OpenImageOptional(args []interface{}) (replyBuf, error) { | ||
return h.openImageImpl(args, true) | ||
return h.openImageImpl(args, openOptions{ | ||
optional: true, | ||
}) | ||
} | ||
|
||
func (h *proxyHandler) CloseImage(args []interface{}) (replyBuf, error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’d find
map[string]any
more natural here — it seems fairly likely we will want to pass non-boolean options in the future.To go further, (preferably?), there probably is a way to use
json.Unmarshal
to deal with parsing the options into a Go struct, not writing any manual parser at all. Maybe something vaguely likeor some even more elaborate option where the handers have well-typed, not type-erased
options
… but that screams YAGNI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
… or, why the … not, we could architecture-astronaut this, and remove the
args
/options
distinction, and all the manual castsHonestly I have no idea if this ends up actually shorter. There are not that many operations that avoiding manual parsing of
args
is clearly a win, and theparsedRequest
implementations are just another kind of boilerplate.The above still feels clumsy, like I’m missing some obvious way to have the standard library do all the work.
(OTOH If we keep going like this, I’m sure we will eventually end up using something like Swagger…)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(
s/any/interface{}/g
, I think we don’t yet require Go 1.18, but that might change soon.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more thought: Get rid of the untyped
Args
entirely, use named parameters only (doesn’t help for any existing API with theArgs []any
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doh, if we unmarshaled the whole request as a method-specific
struct
, we wouldn’t need theopenOptions
sub-struct at all (though it wouldn’t hurt).