[gnoi] SetPackage: auto-resolve version from image when not provided#679
Open
ryanzhu706 wants to merge 2 commits into
Open
[gnoi] SetPackage: auto-resolve version from image when not provided#679ryanzhu706 wants to merge 2 commits into
ryanzhu706 wants to merge 2 commits into
Conversation
When the caller does not provide the version (to_version) field in the SetPackage request, instead of failing with an error, the API now automatically resolves the version by running 'sonic-installer binary_version <filename>' on the installed image. This allows hwproxy and other callers to omit the version field while still getting proper activation (set-default) behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: ryanzhu <ryanzhu@microsoft.com>
Contributor
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the gNOI System.SetPackage implementation to allow callers to omit Package.Version, and (when activation is requested) auto-resolves the version from the installed image using sonic-installer binary_version so it can still run sonic-installer set-default.
Changes:
- Removes strict validation that rejects requests with an empty
Package.Version. - When
activate=trueandversionis empty, resolves version viasonic-installer binary_version <filename>and uses it for activation. - Adds unit tests covering auto-resolution success/failure and the new helper.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| pkg/gnoi/system/system.go | Adds getBinaryVersion() helper and activates with an auto-resolved version when activate=true and version is empty. |
| pkg/gnoi/system/system_test.go | Updates validation expectations so empty version is no longer rejected up-front. |
| pkg/gnoi/system/system_monkey_test.go | Adds monkey-patched unit tests for auto-resolution paths and helper behavior. |
hdwhdw
reviewed
May 21, 2026
Restructure HandleSetPackage to validate and resolve the version from the image binary BEFORE performing install. This ensures we fail early without side effects if version resolution fails. Order is now: validate inputs -> resolve version -> install -> activate Also addresses PR review comments: - Fix args bounds check (len >= 2) before accessing args[1] - Add test for Version='' + Activate=false (install-only) - Update test names to accurately reflect scenarios Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: ryanzhu <ryanzhu@microsoft.com>
Contributor
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Comment on lines
+102
to
+112
| // Resolve version: use provided version, or auto-resolve from image binary | ||
| version := pkg.Package.Version | ||
| if version == "" { | ||
| resolved, err := getBinaryVersion(ctx, pkg.Package.Filename) | ||
| if err != nil { | ||
| log.Errorf("Failed to resolve version from image %s: %v", pkg.Package.Filename, err) | ||
| return nil, status.Errorf(codes.Internal, "failed to resolve version from image: %v", err) | ||
| } | ||
| version = resolved | ||
| log.V(1).Infof("Auto-resolved version from image: %s", version) | ||
| } |
Comment on lines
+174
to
+175
| return "", fmt.Errorf("sonic-installer binary_version failed with exit code %d: %s", | ||
| result.ExitCode, result.Stderr) |
Comment on lines
44
to
54
| @@ -50,8 +50,7 @@ func TestHandleSetPackageValidation(t *testing.T) { | |||
| }, | |||
| }, | |||
| }, | |||
| wantErr: true, | |||
| errMsg: "version is missing", | |||
| wantErr: false, // Version is auto-resolved from image; no longer rejected | |||
| }, | |||
Comment on lines
+135
to
+159
| func TestHandleSetPackage_EmptyVersionNoActivate(t *testing.T) { | ||
| patches := gomonkey.NewPatches() | ||
| defer patches.Reset() | ||
|
|
||
| // Mock: binary_version and install succeed, set-default should NOT be called | ||
| patches.ApplyFunc(exec.RunHostCommand, func(ctx context.Context, cmd string, args []string, opts *exec.RunHostCommandOptions) (*exec.CommandResult, error) { | ||
| if cmd == "sonic-installer" { | ||
| if len(args) >= 1 && args[0] == "binary_version" { | ||
| return &exec.CommandResult{ | ||
| Stdout: "SONiC-OS-4.2.0-Enterprise\n", | ||
| ExitCode: 0, | ||
| }, nil | ||
| } | ||
| if len(args) >= 1 && args[0] == "install" { | ||
| return &exec.CommandResult{ | ||
| Stdout: "Installation completed successfully", | ||
| ExitCode: 0, | ||
| }, nil | ||
| } | ||
| if len(args) >= 1 && args[0] == "set-default" { | ||
| return nil, fmt.Errorf("set-default should not be called when activate=false") | ||
| } | ||
| } | ||
| return nil, fmt.Errorf("unexpected command: %s %v", cmd, args) | ||
| }) |
Comment on lines
+103
to
+133
| func TestHandleSetPackage_AutoResolveVersionFails(t *testing.T) { | ||
| patches := gomonkey.NewPatches() | ||
| defer patches.Reset() | ||
|
|
||
| // Mock: binary_version fails — should reject before install | ||
| patches.ApplyFunc(exec.RunHostCommand, func(ctx context.Context, cmd string, args []string, opts *exec.RunHostCommandOptions) (*exec.CommandResult, error) { | ||
| if cmd == "sonic-installer" && len(args) >= 1 && args[0] == "binary_version" { | ||
| return nil, fmt.Errorf("binary_version command failed") | ||
| } | ||
| return nil, fmt.Errorf("unexpected command (install should not be called): %s %v", cmd, args) | ||
| }) | ||
|
|
||
| ctx := context.Background() | ||
| req := &syspb.SetPackageRequest{ | ||
| Request: &syspb.SetPackageRequest_Package{ | ||
| Package: &syspb.Package{ | ||
| Filename: "/tmp/test-image.bin", | ||
| Version: "", | ||
| Activate: true, | ||
| }, | ||
| }, | ||
| } | ||
|
|
||
| _, err := HandleSetPackage(ctx, req) | ||
| if err == nil { | ||
| t.Fatal("HandleSetPackage() should return error when binary_version fails") | ||
| } | ||
| if !containsSubstring(err.Error(), "failed to resolve version from image") { | ||
| t.Errorf("HandleSetPackage() error = %v, should contain 'failed to resolve version from image'", err) | ||
| } | ||
| } |
hdwhdw
approved these changes
May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description:
Problem
The System.SetPackage gNOI RPC requires the caller to provide the version field. If hwproxy or other callers don't provideit, the request fails with "version is missing in package request". The caller often doesn't know the version string ahead of time — it's embedded in the image binary itself.
Solution
Instead of failing when version is empty, the API now automatically resolves it by running sonic-installer binary_version after installation. This extracts the version from the image and uses it for sonic-installer set-default.
Behavior:
Tests
Why I did it
How I did it
How to verify it
Which release branch to backport (provide reason below if selected)
Description for the changelog
Link to config_db schema for YANG module changes
A picture of a cute animal (not mandatory but encouraged)