Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package google-osconfig-agent for
openSUSE:Factory checked in at 2026-06-27 18:05:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/google-osconfig-agent (Old)
and /work/SRC/openSUSE:Factory/.google-osconfig-agent.new.11887 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "google-osconfig-agent"
Sat Jun 27 18:05:20 2026 rev:55 rq:1361799 version:20260624.01
Changes:
--------
---
/work/SRC/openSUSE:Factory/google-osconfig-agent/google-osconfig-agent.changes
2026-06-17 16:24:18.346087897 +0200
+++
/work/SRC/openSUSE:Factory/.google-osconfig-agent.new.11887/google-osconfig-agent.changes
2026-06-27 18:06:25.496636801 +0200
@@ -1,0 +2,18 @@
+Thu Jun 25 14:34:51 UTC 2026 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to version 20260624.01
+ * Add unit tests for agentendpoint/agentendpoint_beta.go (#983)
+- from version 20260624.00
+ * Replace yum install with yum update (#1009)
+- from version 20260623.02
+ * Bump github.com/containerd/containerd (#1013)
+- from version 20260623.01
+ * Add unit tests for policies/apt.go PART 2 (#957)
+- from version 20260623.00
+ * Add test cases for agentendpoint/task_state.go (#984)
+- from version 20260618.00
+ * Remove deprecated rhel-sap images and add new ones (#1007)
+- from version 20260616.00
+ * Add test cases for clog/clog.go (#986)
+
+-------------------------------------------------------------------
Old:
----
osconfig-20260615.01.tar.gz
New:
----
osconfig-20260624.01.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ google-osconfig-agent.spec ++++++
--- /var/tmp/diff_new_pack.WDbTT6/_old 2026-06-27 18:06:27.608708142 +0200
+++ /var/tmp/diff_new_pack.WDbTT6/_new 2026-06-27 18:06:27.616708412 +0200
@@ -19,7 +19,7 @@
%define shortname osconfig
Name: google-osconfig-agent
-Version: 20260615.01
+Version: 20260624.01
Release: 0
Summary: Google Cloud Guest Agent
License: Apache-2.0
++++++ _service ++++++
--- /var/tmp/diff_new_pack.WDbTT6/_old 2026-06-27 18:06:27.656709763 +0200
+++ /var/tmp/diff_new_pack.WDbTT6/_new 2026-06-27 18:06:27.660709899 +0200
@@ -3,8 +3,8 @@
<param name="url">https://github.com/GoogleCloudPlatform/osconfig</param>
<param name="scm">git</param>
<param name="exclude">.git</param>
- <param name="versionformat">20260615.01</param>
- <param name="revision">20260615.01</param>
+ <param name="versionformat">20260624.01</param>
+ <param name="revision">20260624.01</param>
<param name="changesgenerate">enable</param>
</service>
<service name="recompress" mode="disabled">
@@ -15,7 +15,7 @@
<param name="basename">osconfig</param>
</service>
<service name="go_modules" mode="disabled">
- <param name="archive">osconfig-20260615.01.tar.gz</param>
+ <param name="archive">osconfig-20260624.01.tar.gz</param>
</service>
</services>
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.WDbTT6/_old 2026-06-27 18:06:27.684710709 +0200
+++ /var/tmp/diff_new_pack.WDbTT6/_new 2026-06-27 18:06:27.688710844 +0200
@@ -1,6 +1,6 @@
<servicedata>
<service name="tar_scm">
<param
name="url">https://github.com/GoogleCloudPlatform/osconfig</param>
- <param
name="changesrevision">5b74d2e70f06700a7a5b1901efd6f602d6ad0b84</param></service></servicedata>
+ <param
name="changesrevision">ba0531cd3c14cb3fee9b40bde6ce8203c3abade3</param></service></servicedata>
(No newline at EOF)
++++++ osconfig-20260615.01.tar.gz -> osconfig-20260624.01.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/osconfig-20260615.01/agentendpoint/agentendpoint_beta.go
new/osconfig-20260624.01/agentendpoint/agentendpoint_beta.go
--- old/osconfig-20260615.01/agentendpoint/agentendpoint_beta.go
2026-06-15 22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/agentendpoint/agentendpoint_beta.go
2026-06-24 17:10:57.000000000 +0200
@@ -26,6 +26,7 @@
"github.com/GoogleCloudPlatform/osconfig/osinfo"
"github.com/GoogleCloudPlatform/osconfig/pretty"
"github.com/GoogleCloudPlatform/osconfig/retryutil"
+ "github.com/googleapis/gax-go/v2"
"google.golang.org/api/option"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -33,9 +34,14 @@
"cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb"
)
+type agentEndpointBetaClient interface {
+ LookupEffectiveGuestPolicy(context.Context,
*agentendpointpb.LookupEffectiveGuestPolicyRequest, ...gax.CallOption)
(*agentendpointpb.EffectiveGuestPolicy, error)
+ Close() error
+}
+
// BetaClient is a an agentendpoint client.
type BetaClient struct {
- raw *agentendpoint.Client
+ raw agentEndpointBetaClient
cancel context.CancelFunc
noti chan struct{}
closed bool
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/osconfig-20260615.01/agentendpoint/agentendpoint_beta_test.go
new/osconfig-20260624.01/agentendpoint/agentendpoint_beta_test.go
--- old/osconfig-20260615.01/agentendpoint/agentendpoint_beta_test.go
1970-01-01 01:00:00.000000000 +0100
+++ new/osconfig-20260624.01/agentendpoint/agentendpoint_beta_test.go
2026-06-24 17:10:57.000000000 +0200
@@ -0,0 +1,90 @@
+// Copyright 2026 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package agentendpoint
+
+import (
+ "errors"
+ "fmt"
+ "testing"
+
+ "cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb"
+ utilmocks "github.com/GoogleCloudPlatform/osconfig/util/mocks"
+ "github.com/GoogleCloudPlatform/osconfig/util/utiltest"
+ "github.com/golang/mock/gomock"
+)
+
+func TestBetaClientClose(t *testing.T) {
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+
+ mockClient := utilmocks.NewMockAgentEndpointBetaClient(ctrl)
+ client := &BetaClient{raw: mockClient, noti: make(chan struct{}, 1)}
+
+ if client.Closed() {
+ t.Errorf("Closed() = true, want false")
+ }
+
+ mockClient.EXPECT().Close().Return(nil)
+
+ if err := client.Close(); err != nil {
+ t.Errorf("Close() returned unexpected error: %v", err)
+ }
+
+ if !client.Closed() {
+ t.Errorf("Closed() = false, want true")
+ }
+}
+
+func TestLookupEffectiveGuestPolicies(t *testing.T) {
+ ctx := t.Context()
+ ctrl := gomock.NewController(t)
+ defer ctrl.Finish()
+
+ mockClient := utilmocks.NewMockAgentEndpointBetaClient(ctrl)
+ client := &BetaClient{raw: mockClient, noti: make(chan struct{}, 1)}
+
+ tests := []struct {
+ name string
+ setup func(*testing.T)
+ wantPolicy *agentendpointpb.EffectiveGuestPolicy
+ wantErr error
+ }{
+ {
+ name: "successful server response, expect non-nil
policy",
+ setup: func(t *testing.T) {
+
mockClient.EXPECT().LookupEffectiveGuestPolicy(gomock.Any(),
gomock.Any()).Return(&agentendpointpb.EffectiveGuestPolicy{}, nil)
+ },
+ wantPolicy: &agentendpointpb.EffectiveGuestPolicy{},
+ wantErr: nil,
+ },
+ {
+ name: "server returns error, expect error",
+ setup: func(t *testing.T) {
+
mockClient.EXPECT().LookupEffectiveGuestPolicy(gomock.Any(),
gomock.Any()).Return(nil, errors.New("mock error"))
+ },
+ wantErr: fmt.Errorf("error calling
LookupEffectiveGuestPolicies: %w", errors.New("mock error")),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tt.setup(t)
+
+ gotPolicy, gotErr :=
client.LookupEffectiveGuestPolicies(ctx)
+ utiltest.AssertErrorMatch(t, gotErr, tt.wantErr)
+ utiltest.AssertEquals(t, gotPolicy, tt.wantPolicy)
+ })
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/osconfig-20260615.01/agentendpoint/agentendpoint_test.go
new/osconfig-20260624.01/agentendpoint/agentendpoint_test.go
--- old/osconfig-20260615.01/agentendpoint/agentendpoint_test.go
2026-06-15 22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/agentendpoint/agentendpoint_test.go
2026-06-24 17:10:57.000000000 +0200
@@ -334,8 +334,6 @@
defer os.RemoveAll(td)
taskStateFile = filepath.Join(td, "testState")
- srv.streamSend <- struct{}{}
-
// No state.
if err := tc.client.loadTaskFromState(ctx); err != nil {
t.Error(err)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/osconfig-20260615.01/agentendpoint/task_state_test.go
new/osconfig-20260624.01/agentendpoint/task_state_test.go
--- old/osconfig-20260615.01/agentendpoint/task_state_test.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/agentendpoint/task_state_test.go 2026-06-24
17:10:57.000000000 +0200
@@ -15,16 +15,23 @@
package agentendpoint
import (
+ "encoding/json"
+ "errors"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
+ "reflect"
+ "syscall"
"testing"
+ "time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/testing/protocmp"
"cloud.google.com/go/osconfig/agentendpoint/apiv1/agentendpointpb"
+ "github.com/GoogleCloudPlatform/osconfig/util/utiltest"
)
var (
@@ -63,33 +70,53 @@
// We don't test execTask as reboots during that task type is not
supported.
var tests = []struct {
name string
- state []byte
- wantErr bool
+ setup func(t *testing.T) string
+ wantErr error
want *taskState
}{
{
- "BlankState",
- []byte("{}"),
- false,
- &taskState{},
+ name: "empty json, want blank state",
+ setup: func(t *testing.T) string {
+ if err := ioutil.WriteFile(testState,
[]byte("{}"), 0600); err != nil {
+ t.Fatalf("error writing state: %v", err)
+ }
+ return testState
+ },
+ wantErr: nil,
+ want: &taskState{},
},
{
- "BadState",
- []byte("foo"),
- true,
- &taskState{},
+ name: "invalid json, want json syntax error",
+ setup: func(t *testing.T) string {
+ if err := ioutil.WriteFile(testState,
[]byte("foo"), 0600); err != nil {
+ t.Fatalf("error writing state: %v", err)
+ }
+ return testState
+ },
+ wantErr: json.Unmarshal([]byte("foo"), &taskState{}),
+ want: &taskState{},
},
{
- "PatchTask",
- []byte(testPatchTaskStateString),
- false,
- testPatchTaskState,
+ name: "valid patch task json, want patch task state",
+ setup: func(t *testing.T) string {
+ if err := ioutil.WriteFile(testState,
[]byte(testPatchTaskStateString), 0600); err != nil {
+ t.Fatalf("error writing state: %v", err)
+ }
+ return testState
+ },
+ wantErr: nil,
+ want: testPatchTaskState,
},
{
- "IgnoresOldRebootFieldName",
-
[]byte("{\"PatchTask\":{\"Task\":{},\"RebootCount\":1}}"),
- false,
- &taskState{
+ name: "json with old reboot field, want state with zero
reboot counts",
+ setup: func(t *testing.T) string {
+ if err := ioutil.WriteFile(testState,
[]byte("{\"PatchTask\":{\"Task\":{},\"RebootCount\":1}}"), 0600); err != nil {
+ t.Fatalf("error writing state: %v", err)
+ }
+ return testState
+ },
+ wantErr: nil,
+ want: &taskState{
PatchTask: &patchTask{
Task: &applyPatchesTask{
&agentendpointpb.ApplyPatchesTask{},
@@ -99,20 +126,20 @@
},
},
},
+ {
+ name: "directory path, want path error",
+ setup: func(t *testing.T) string {
+ return td
+ },
+ wantErr: &fs.PathError{Op: "read", Path: td, Err:
syscall.EISDIR},
+ want: nil,
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- if err := ioutil.WriteFile(testState, tt.state, 0600);
err != nil {
- t.Fatalf("error writing state: %v", err)
- }
-
- st, err := loadState(testState)
- if err != nil && !tt.wantErr {
- t.Fatalf("unexpected error: %v", err)
- }
- if err == nil && tt.wantErr {
- t.Fatalf("expected error")
- }
+ path := tt.setup(t)
+ st, err := loadState(path)
+ utiltest.AssertErrorMatch(t, err, tt.wantErr)
if diff := cmp.Diff(tt.want, st,
cmpopts.IgnoreUnexported(patchTask{}), protocmp.Transform()); diff != "" {
t.Errorf("State does not match expectation:
(-got +want)\n%s", diff)
@@ -145,55 +172,71 @@
}
func TestStateSave(t *testing.T) {
- td, err := ioutil.TempDir(os.TempDir(), "")
- if err != nil {
- t.Fatalf("error creating temp dir: %v", err)
- }
- defer os.RemoveAll(td)
+ td := t.TempDir()
testState := filepath.Join(td, "testState")
+ invalidDir := utiltest.WriteToTempFileMust(t, "invalidDir", []byte(""))
+ invalidPath := filepath.Join(invalidDir, "testState")
var tests = []struct {
- desc string
- state *taskState
- want string
+ desc string
+ state *taskState
+ path string
+ want string
+ wantErr error
}{
{
- "NilState",
- nil,
- "{}",
+ desc: "nil state, expect empty json",
+ state: nil,
+ path: testState,
+ want: "{}",
+ wantErr: nil,
+ },
+ {
+ desc: "blank state, expect empty json",
+ state: &taskState{},
+ path: testState,
+ want: "{}",
+ wantErr: nil,
},
{
- "BlankState",
- &taskState{},
- "{}",
+ desc: "patch task state, expect serialized patch
task json",
+ state: testPatchTaskState,
+ path: testState,
+ want: testPatchTaskStateString,
+ wantErr: nil,
},
{
- "PatchTask",
- testPatchTaskState,
- testPatchTaskStateString,
+ desc: "exec task state, expect serialized exec task
json",
+ state: &taskState{ExecTask: &execTask{TaskID: "foo"}},
+ path: testState,
+ want:
"{\"ExecTask\":{\"StartedAt\":\"0001-01-01T00:00:00Z\",\"Task\":null,\"TaskID\":\"foo\"}}",
+ wantErr: nil,
},
{
- "ExecTask",
- &taskState{ExecTask: &execTask{TaskID: "foo"}},
-
"{\"ExecTask\":{\"StartedAt\":\"0001-01-01T00:00:00Z\",\"Task\":null,\"TaskID\":\"foo\"}}",
+ desc: "invalid directory path, expect path error",
+ state: &taskState{},
+ path: invalidPath,
+ want: "",
+ wantErr: &fs.PathError{Op: "mkdir", Path: invalidDir,
Err: errors.New("not a directory")},
+ },
+ {
+ // time.Time.MarshalJSON only supports years between 0
and 9999.
+ desc: "invalid input for json, expect marshal error",
+ state: &taskState{
+ ExecTask: &execTask{StartedAt: time.Date(10000,
1, 1, 0, 0, 0, 0, time.UTC)},
+ },
+ path: testState,
+ want: "",
+ wantErr: &json.MarshalerError{Type:
reflect.TypeOf(time.Time{}), Err: errors.New("Time.MarshalJSON: year outside of
range [0,9999]")},
},
}
for _, tt := range tests {
- err := tt.state.save(testState)
- if err != nil {
- t.Errorf("%s: unexpected save error: %v", tt.desc, err)
- continue
- }
-
- got, err := ioutil.ReadFile(testState)
- if err != nil {
- t.Errorf("%s: error reading state: %v", tt.desc, err)
- continue
- }
-
- if string(got) != tt.want {
- t.Errorf("%s:\ngot:\n%q\nwant:\n%q", tt.desc, got,
tt.want)
- }
+ t.Run(tt.desc, func(t *testing.T) {
+ gotErr := tt.state.save(tt.path)
+
+ utiltest.AssertErrorMatchAndSkip(t, gotErr, tt.wantErr)
+ utiltest.AssertFileContents(t, tt.path, tt.want)
+ })
}
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/clog/clog_test.go
new/osconfig-20260624.01/clog/clog_test.go
--- old/osconfig-20260615.01/clog/clog_test.go 2026-06-15 22:34:32.000000000
+0200
+++ new/osconfig-20260624.01/clog/clog_test.go 2026-06-24 17:10:57.000000000
+0200
@@ -16,13 +16,19 @@
import (
"context"
+ "fmt"
+ "io"
"testing"
+ "github.com/GoogleCloudPlatform/guest-logging-go/logger"
+ "github.com/GoogleCloudPlatform/osconfig/util/utiltest"
"github.com/google/go-cmp/cmp"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/known/wrapperspb"
)
func TestWithLabels(t *testing.T) {
- ctx := context.Background()
+ ctx := t.Context()
tests := []struct {
name string
@@ -44,3 +50,126 @@
})
}
}
+
+type testWriter struct {
+ logs string
+}
+
+func (w *testWriter) Write(p []byte) (n int, err error) {
+ w.logs = string(p)
+ return len(p), nil
+}
+
+// Initializes logger and returns testWriter
+func initTestLogger(ctx context.Context, t *testing.T) *testWriter {
+ t.Helper()
+ tw := &testWriter{}
+ err := logger.Init(ctx, logger.LogOpts{
+ LoggerName: "test-logger",
+ Writers: []io.Writer{tw},
+ DisableCloudLogging: true,
+ DisableLocalLogging: true,
+ Debug: true,
+ FormatFunction: func(e logger.LogEntry) string {
+ return fmt.Sprintf("[%s] %s", e.Severity, e.Message)
+ },
+ })
+ if err != nil {
+ t.Fatalf("logger.Init error: %v", err)
+ }
+ return tw
+}
+
+func TestDebugRPC(t *testing.T) {
+ utiltest.OverrideVariable(t, &DebugEnabled, true)
+ ctx := t.Context()
+ tw := initTestLogger(ctx, t)
+ req := wrapperspb.String("req message")
+ resp := wrapperspb.String("resp message")
+
+ tests := []struct {
+ name string
+ req proto.Message
+ resp proto.Message
+ want string
+ }{
+ {
+ name: "ReqAndRespMethod",
+ req: req,
+ resp: resp,
+ want: `(?s)^\[Debug\] Called: ReqAndRespMethod with
request:.*"req message".*\nresponse:\n.*"resp message".*`,
+ },
+ {
+ name: "RespOnlyMethod",
+ req: nil,
+ resp: resp,
+ want: `(?s)^\[Debug\] Called: RespOnlyMethod with
response:\n.*"resp message".*`,
+ },
+ {
+ name: "ReqOnlyMethod",
+ req: req,
+ resp: nil,
+ want: `(?s)^\[Debug\] Calling: ReqOnlyMethod with
request:\n.*"req message".*`,
+ },
+ {
+ name: "NoReqNoRespMethod",
+ req: nil,
+ resp: nil,
+ want: `^$`,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tw.logs = ""
+ DebugRPC(ctx, tt.name, tt.req, tt.resp)
+ utiltest.AssertFormatMatch(t, tw.logs, tt.want)
+ })
+ }
+}
+
+func TestLoggingFunctions(t *testing.T) {
+ ctx := t.Context()
+ tw := initTestLogger(ctx, t)
+ tests := []struct {
+ name string
+ logFunc func(ctx context.Context)
+ want string
+ }{
+ {
+ name: "Debugf",
+ logFunc: func(ctx context.Context) { Debugf(ctx, "test
debug %s", "msg") },
+ want: "[Debug] test debug msg\n",
+ },
+ {
+ name: "Infof",
+ logFunc: func(ctx context.Context) { Infof(ctx, "test
info %s", "msg") },
+ want: "[Info] test info msg\n",
+ },
+ {
+ name: "Warningf",
+ logFunc: func(ctx context.Context) { Warningf(ctx,
"test warn %s", "msg") },
+ want: "[Warning] test warn msg\n",
+ },
+ {
+ name: "Errorf",
+ logFunc: func(ctx context.Context) { Errorf(ctx, "test
error %s", "msg") },
+ want: "[Error] test error msg\n",
+ },
+ {
+ name: "DebugStructured",
+ logFunc: func(ctx context.Context) {
+ DebugStructured(ctx, map[string]string{"key":
"value"}, "test structured %s", "msg")
+ },
+ want: "[Debug] test structured msg\n",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tw.logs = ""
+ tt.logFunc(ctx)
+ utiltest.AssertEquals(t, tw.logs, tt.want)
+ })
+ }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/e2e_tests/utils/utils.go
new/osconfig-20260624.01/e2e_tests/utils/utils.go
--- old/osconfig-20260615.01/e2e_tests/utils/utils.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/e2e_tests/utils/utils.go 2026-06-24
17:10:57.000000000 +0200
@@ -391,7 +391,6 @@
var HeadEL8Images = map[string]string{
"rhel-cloud/rhel-8": "projects/rhel-cloud/global/images/family/rhel-8",
- "rhel-sap-cloud/rhel-8-6-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-8-6-sap-ha",
"rhel-sap-cloud/rhel-8-8-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-8-8-sap-ha",
"rhel-sap-cloud/rhel-8-10-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-8-10-sap-ha",
@@ -410,8 +409,9 @@
"rhel-cloud/rhel-9": "projects/rhel-cloud/global/images/family/rhel-9",
- "rhel-sap-cloud/rhel-9-0-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-9-0-sap-ha",
"rhel-sap-cloud/rhel-9-2-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-9-2-sap-ha",
+ "rhel-sap-cloud/rhel-9-4-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-9-4-sap-ha",
+ "rhel-sap-cloud/rhel-9-6-sap":
"projects/rhel-sap-cloud/global/images/family/rhel-9-6-sap-ha",
"rocky-linux-cloud/rocky-linux-9":
"projects/rocky-linux-cloud/global/images/family/rocky-linux-9",
"rocky-linux-cloud/rocky-linux-9-optimized-gcp":
"projects/rocky-linux-cloud/global/images/family/rocky-linux-9-optimized-gcp",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/go.mod
new/osconfig-20260624.01/go.mod
--- old/osconfig-20260615.01/go.mod 2026-06-15 22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/go.mod 2026-06-24 17:10:57.000000000 +0200
@@ -63,7 +63,7 @@
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/compose-spec/compose-go/v2 v2.8.1 // indirect
github.com/containerd/cgroups/v3 v3.1.2 // indirect
- github.com/containerd/containerd v1.7.32 // indirect
+ github.com/containerd/containerd v1.7.33 // indirect
github.com/containerd/containerd/api v1.10.0 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/go.sum
new/osconfig-20260624.01/go.sum
--- old/osconfig-20260615.01/go.sum 2026-06-15 22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/go.sum 2026-06-24 17:10:57.000000000 +0200
@@ -141,8 +141,8 @@
github.com/compose-spec/compose-go/v2 v2.8.1/go.mod
h1:veko/VB7URrg/tKz3vmIAQDaz+CGiXH8vZsW79NmAww=
github.com/containerd/cgroups/v3 v3.1.2
h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
github.com/containerd/cgroups/v3 v3.1.2/go.mod
h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
-github.com/containerd/containerd v1.7.32
h1:S54xuVcPxeLaYgaRABtpJ2VyVUVsy0IGf7qHBs+sbY8=
-github.com/containerd/containerd v1.7.32/go.mod
h1:jdwD6s/BhV4XVJGrvtziNPVA+83n66TwptVaPKprq4E=
+github.com/containerd/containerd v1.7.33
h1:iAkYGC/ifR/V+0eR4iXWHNGYUF0DF2PmGV5iz4Irj5M=
+github.com/containerd/containerd v1.7.33/go.mod
h1:gSbSCVjPCdkfJCjyrzz7aRC+xFlqVbatNpfHfVCYGUM=
github.com/containerd/containerd/api v1.10.0
h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
github.com/containerd/containerd/api v1.10.0/go.mod
h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
github.com/containerd/continuity v0.4.5
h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/ospatch/yum_update.go
new/osconfig-20260624.01/ospatch/yum_update.go
--- old/osconfig-20260615.01/ospatch/yum_update.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/ospatch/yum_update.go 2026-06-24
17:10:57.000000000 +0200
@@ -131,7 +131,7 @@
logOps(ctx, ops)
- err = packages.InstallYumPackages(ctx, pkgNames)
+ err = packages.UpdateYumPackages(ctx, pkgNames)
if err == nil {
logSuccess(ctx, ops)
} else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/ospatch/yum_update_test.go
new/osconfig-20260624.01/ospatch/yum_update_test.go
--- old/osconfig-20260615.01/ospatch/yum_update_test.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/ospatch/yum_update_test.go 2026-06-24
17:10:57.000000000 +0200
@@ -44,7 +44,7 @@
packages.SetCommandRunner(mockCommandRunner)
checkUpdateCall := mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"check-update",
"--assumeyes"}...))).Return([]byte("stdout"), []byte("stderr"),
errExit100).Times(1)
// yum install call to install package
- mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"install", "--assumeyes",
"foo.noarch"}...))).After(checkUpdateCall).Return([]byte("stdout"),
[]byte("stderr"), nil).Times(1)
+ mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"update", "--assumeyes",
"foo.noarch"}...))).After(checkUpdateCall).Return([]byte("stdout"),
[]byte("stderr"), nil).Times(1)
packages.SetPtyCommandRunner(mockCommandRunner)
mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"update", "--assumeno",
"--color=never", "--security"}...))).Return(data, []byte("stderr"),
nil).Times(1)
@@ -81,7 +81,7 @@
packages.SetCommandRunner(mockCommandRunner)
checkUpdateCall := mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"check-update",
"--assumeyes"}...))).Return([]byte("stdout"), []byte("stderr"),
errExit100).Times(1)
// yum install call to install package, make sure only 2 packages are
installed.
- mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"install", "--assumeyes",
"foo.noarch",
"bar.x86_64"}...))).After(checkUpdateCall).Return([]byte("stdout"),
[]byte("stderr"), nil).Times(1)
+ mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"update", "--assumeyes",
"foo.noarch",
"bar.x86_64"}...))).After(checkUpdateCall).Return([]byte("stdout"),
[]byte("stderr"), nil).Times(1)
packages.SetPtyCommandRunner(mockCommandRunner)
mockCommandRunner.EXPECT().Run(ctx,
utilmocks.EqCmd(exec.Command("/usr/bin/yum", []string{"update", "--assumeno",
"--color=never", "--security"}...))).Return(data, []byte("stderr"),
nil).Times(1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/packages/yum.go
new/osconfig-20260624.01/packages/yum.go
--- old/osconfig-20260615.01/packages/yum.go 2026-06-15 22:34:32.000000000
+0200
+++ new/osconfig-20260624.01/packages/yum.go 2026-06-24 17:10:57.000000000
+0200
@@ -34,6 +34,7 @@
yumInstallArgs = []string{"install", "--assumeyes"}
yumRemoveArgs = []string{"remove", "--assumeyes"}
+ yumUpdateArgs = []string{"update", "--assumeyes"}
yumCheckUpdateArgs = []string{"check-update", "--assumeyes"}
yumListUpdatesArgs = []string{"update", "--assumeno",
"--color=never"}
yumListUpdateMinimalArgs = []string{"update-minimal", "--assumeno",
"--color=never"}
@@ -76,6 +77,12 @@
return err
}
+// UpdateYumPackages updates yum packages.
+func UpdateYumPackages(ctx context.Context, pkgs []string) error {
+ _, err := run(ctx, yum, append(yumUpdateArgs, pkgs...))
+ return err
+}
+
// RemoveYumPackages removes yum packages.
func RemoveYumPackages(ctx context.Context, pkgs []string) error {
_, err := run(ctx, yum, append(yumRemoveArgs, pkgs...))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/packages/yum_test.go
new/osconfig-20260624.01/packages/yum_test.go
--- old/osconfig-20260615.01/packages/yum_test.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/packages/yum_test.go 2026-06-24
17:10:57.000000000 +0200
@@ -45,6 +45,25 @@
}
}
+func TestUpdateYumPackages(t *testing.T) {
+ mockCtrl := gomock.NewController(t)
+ defer mockCtrl.Finish()
+
+ mockCommandRunner := utilmocks.NewMockCommandRunner(mockCtrl)
+ runner = mockCommandRunner
+ expectedCmd := utilmocks.EqCmd(exec.Command(yum, append(yumUpdateArgs,
pkgs...)...))
+
+ mockCommandRunner.EXPECT().Run(testCtx,
expectedCmd).Return([]byte("stdout"), []byte("stderr"), nil).Times(1)
+ if err := UpdateYumPackages(testCtx, pkgs); err != nil {
+ t.Errorf("unexpected error: %v", err)
+ }
+
+ mockCommandRunner.EXPECT().Run(testCtx,
expectedCmd).Return([]byte("stdout"), []byte("stderr"), errors.New("could not
update")).Times(1)
+ if err := UpdateYumPackages(testCtx, pkgs); err == nil {
+ t.Errorf("did not get expected error")
+ }
+}
+
func TestRemoveYum(t *testing.T) {
ctx := context.Background()
mockCtrl := gomock.NewController(t)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/policies/apt_test.go
new/osconfig-20260624.01/policies/apt_test.go
--- old/osconfig-20260615.01/policies/apt_test.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/policies/apt_test.go 2026-06-24
17:10:57.000000000 +0200
@@ -15,140 +15,307 @@
package policies
import (
+ "bytes"
"context"
"errors"
- "fmt"
- "io/ioutil"
- "os"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
"os/exec"
"path/filepath"
- "strings"
"testing"
"cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb"
+ "github.com/GoogleCloudPlatform/guest-logging-go/logger"
"github.com/GoogleCloudPlatform/osconfig/osinfo"
"github.com/GoogleCloudPlatform/osconfig/packages"
utilmocks "github.com/GoogleCloudPlatform/osconfig/util/mocks"
"github.com/GoogleCloudPlatform/osconfig/util/utiltest"
"github.com/golang/mock/gomock"
"golang.org/x/crypto/openpgp"
+ "golang.org/x/crypto/openpgp/armor"
+ openpgp_errors "golang.org/x/crypto/openpgp/errors"
"golang.org/x/crypto/openpgp/packet"
)
-func runAptRepositories(ctx context.Context, repos
[]*agentendpointpb.AptRepository) (string, error) {
- td, err := ioutil.TempDir(os.TempDir(), "")
+func createTestGPGKey(t *testing.T) []byte {
+ t.Helper()
+ entity, err := openpgp.NewEntity("test", "test", "[email protected]", nil)
if err != nil {
- return "", fmt.Errorf("error creating temp dir: %v", err)
+ t.Fatal(err)
}
- defer os.RemoveAll(td)
- testRepo := filepath.Join(td, "testRepo")
-
- if err := aptRepositories(ctx, repos, testRepo); err != nil {
- return "", fmt.Errorf("error running aptRepositories: %v", err)
+ var buf bytes.Buffer
+ if err := entity.Serialize(&buf); err != nil {
+ t.Fatal(err)
}
+ return buf.Bytes()
+}
- data, err := ioutil.ReadFile(testRepo)
+// createTestArmoredGPGKey generates a valid armored GPG key for testing.
+func createTestArmoredGPGKey(t *testing.T) []byte {
+ t.Helper()
+ entity, err := openpgp.NewEntity("test", "test", "[email protected]", nil)
if err != nil {
- return "", fmt.Errorf("error reading testRepo: %v", err)
+ t.Fatal(err)
+ }
+ var buf bytes.Buffer
+ w, err := armor.Encode(&buf, openpgp.PublicKeyType, nil)
+ if err != nil {
+ t.Fatal(err)
}
+ if err := entity.Serialize(w); err != nil {
+ t.Fatal(err)
+ }
+ w.Close()
+ return buf.Bytes()
+}
+
+// setupAptRepositoriesTest sets up a test server and a test repository path.
+func setupAptRepositoriesTest(t *testing.T) (string, string) {
+ t.Helper()
+ validKey := createTestGPGKey(t)
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
+ switch r.URL.Path {
+ case "/valid-key":
+ w.Write(validKey)
+ default:
+ w.Write([]byte("fake-gpg-key"))
+ }
+ }))
+ t.Cleanup(srv.Close)
- return string(data), nil
+ td := t.TempDir()
+ testRepo := filepath.Join(td, "testRepo")
+
+ return srv.URL, testRepo
}
+// TestAptRepositories tests the adding of apt repository files.
func TestAptRepositories(t *testing.T) {
- debian10 := func() (string, string, string) {
- return "debian", "Debian", "10"
- }
-
- debian12 := func() (string, string, string) {
- return "debian", "Debian", "12"
- }
+ ctx := context.Background()
+ srvURL, testRepo := setupAptRepositoriesTest(t)
tests := []struct {
- name string
- repos []*agentendpointpb.AptRepository
- nameAndVersionProvider func() (string, string, string)
- want string
+ name string
+ repos []*agentendpointpb.AptRepository
+ provider osinfo.Provider
+ repoFile string
+ wantRepo string
+ wantLogsRegexp string
}{
{
- name: "No repositories",
- nameAndVersionProvider: debian10,
- repos:
[]*agentendpointpb.AptRepository{},
- want: "# Repo file managed by Google
OSConfig agent\n"},
+ name: "no repositories, want header only",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
+ repos: []*agentendpointpb.AptRepository{},
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n",
+ wantLogsRegexp: "",
+ },
{
- name: "1 repositoy, Debian 10",
- nameAndVersionProvider: debian10,
+ name: "single deb repo on debian 10, want repo line
without signed-by",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
repos: []*agentendpointpb.AptRepository{
{Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}},
},
- want: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo1-url/ distribution component1\n",
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo1-url/ distribution component1\n",
+ wantLogsRegexp: "",
},
{
- name: "1 repositoy, Debian 12",
- nameAndVersionProvider: debian12,
+ name: "single deb repo on debian 12, want repo line
with signed-by",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "12"
+ }},
repos: []*agentendpointpb.AptRepository{
{Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}},
},
- want: "# Repo file managed by Google OSConfig
agent\n\ndeb [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg]
http://repo1-url/ distribution component1\n",
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg]
http://repo1-url/ distribution component1\n",
+ wantLogsRegexp: "",
+ },
+ {
+ name: "unknown archive type, want default to deb",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
+ repos: []*agentendpointpb.AptRepository{
+ {Uri: "http://repo", Distribution: "dist",
ArchiveType: agentendpointpb.AptRepository_ArchiveType(99)},
+ },
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo dist\n",
+ wantLogsRegexp: "",
+ },
+ {
+ name: "multiple repos and components, want multiple
repo lines",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
+ repos: []*agentendpointpb.AptRepository{
+ {Uri: "http://repo1", Distribution: "dist1",
Components: []string{"comp1"}, ArchiveType:
agentendpointpb.AptRepository_DEB_SRC},
+ {Uri: "http://repo2", Distribution: "dist2",
Components: []string{"comp1", "comp2"}},
+ },
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb-src http://repo1 dist1 comp1\n\ndeb http://repo2 dist2 comp1
comp2\n",
+ wantLogsRegexp: "",
},
{
- name: "2 repos, Debian 10",
- nameAndVersionProvider: debian10,
+ name: "repo with valid gpg key, want repo line and gpg
block coverage",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
repos: []*agentendpointpb.AptRepository{
- {Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}, ArchiveType:
agentendpointpb.AptRepository_DEB_SRC},
- {Uri: "http://repo2-url/", Distribution:
"distribution", Components: []string{"component1", "component2"}, ArchiveType:
agentendpointpb.AptRepository_DEB},
+ {Uri: "http://repo", Distribution: "dist",
GpgKey: srvURL + "/valid-key"},
},
- want: "# Repo file managed by Google OSConfig
agent\n\ndeb-src http://repo1-url/ distribution component1\n\ndeb
http://repo2-url/ distribution component1 component2\n",
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo dist\n",
+ wantLogsRegexp: "",
},
{
- name: "2 repos, Debian 12",
- nameAndVersionProvider: debian12,
+ name: "osinfo error, want repo line without
signed-by",
+ provider: stubOsInfoProvider{err: errors.New("osinfo
error")},
repos: []*agentendpointpb.AptRepository{
- {Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}, ArchiveType:
agentendpointpb.AptRepository_DEB_SRC},
- {Uri: "http://repo2-url/", Distribution:
"distribution", Components: []string{"component1", "component2"}, ArchiveType:
agentendpointpb.AptRepository_DEB},
+ {Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}},
},
- want: "# Repo file managed by Google OSConfig
agent\n\ndeb-src [signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg]
http://repo1-url/ distribution component1\n\ndeb
[signed-by=/etc/apt/trusted.gpg.d/osconfig_agent_managed.gpg] http://repo2-url/
distribution component1 component2\n",
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo1-url/ distribution component1\n",
+ wantLogsRegexp: "",
+ },
+ {
+ name: "invalid os version, want repo line without
signed-by",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "invalid"
+ }},
+ repos: []*agentendpointpb.AptRepository{
+ {Uri: "http://repo1-url/", Distribution:
"distribution", Components: []string{"component1"}},
+ },
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo1-url/ distribution component1\n",
+ wantLogsRegexp: "",
+ },
+ {
+ name: "gpg key fetch error, want error fetching gpg
key",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
+ repos: []*agentendpointpb.AptRepository{
+ {Uri: "http://repo", Distribution: "dist",
GpgKey: "http://invalid-url"},
+ },
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo dist\n",
+ wantLogsRegexp: `.*Error fetching gpg key
"http://invalid-url": Get "http://invalid-url": dial tcp: lookup invalid-url.*`,
+ },
+ {
+ name: "invalid gpg key data, want error fetching gpg
key",
+ provider: stubOsInfoProvider{nameVersionProvider:
func() (string, string, string) {
+ return "debian", "Debian", "10"
+ }},
+ repos: []*agentendpointpb.AptRepository{
+ {Uri: "http://repo", Distribution: "dist",
GpgKey: srvURL + "/invalid-key"},
+ },
+ wantRepo: "# Repo file managed by Google OSConfig
agent\n\ndeb http://repo dist\n",
+ wantLogsRegexp: `.*Error fetching gpg key
"http://127.0.0.1:.*/invalid-key": openpgp: invalid data: tag byte does not
have MSB set.*`,
},
}
for _, tt := range tests {
- osInfoProviderActual := osInfoProvider
- defer func() { osInfoProvider = osInfoProviderActual }()
-
- osInfoStub := stubOsInfoProvider{nameVersionProvider:
tt.nameAndVersionProvider}
- osInfoProvider = osInfoStub
-
- got, err := runAptRepositories(context.Background(), tt.repos)
- if err != nil {
- t.Fatal(err)
- }
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ _ = logger.Init(ctx, logger.LogOpts{LoggerName:
"trace_test", Debug: true, Writers: []io.Writer{&buf}})
+ utiltest.OverrideVariable(t, &osInfoProvider,
tt.provider)
+ aptRepositories(ctx, tt.repos, testRepo)
+
+ utiltest.AssertFileContents(t, testRepo, tt.wantRepo)
+ utiltest.AssertFormatMatch(t, buf.String(),
tt.wantLogsRegexp)
+ })
+ }
+}
- if got != tt.want {
- t.Errorf("%s: got:\n%q\nwant:\n%q", tt.name, got,
tt.want)
+// setupGetAptGPGKeyTest sets up a test server for GPG key retrieval tests.
+func setupGetAptGPGKeyTest(t *testing.T) string {
+ t.Helper()
+ validBinaryKey := createTestGPGKey(t)
+ validArmoredKey := createTestArmoredGPGKey(t)
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,
r *http.Request) {
+ switch r.URL.Path {
+ case "/valid_binary":
+ w.Write(validBinaryKey)
+ case "/valid_armored":
+ w.Write(validArmoredKey)
+ case "/large":
+ w.Header().Set("Content-Length", "2000000")
+ w.Write(make([]byte, 100))
+ case "/binary":
+ w.Write([]byte{0x99, 0x01, 0x02})
+ case "/empty_armored":
+ w.Write([]byte("-----BEGIN PGP PUBLIC KEY
BLOCK-----\n\n-----END PGP PUBLIC KEY BLOCK-----"))
+ default:
+ w.Write([]byte("invalid data"))
}
- }
+ }))
+ t.Cleanup(srv.Close)
+ return srv.URL
}
+// TestGetAptGPGKey tests the retrieval and validation of apt GPG keys.
func TestGetAptGPGKey(t *testing.T) {
- key := "https://packages.cloud.google.com/apt/doc/apt-key.gpg"
+ srvURL := setupGetAptGPGKeyTest(t)
- entityList, err := getAptGPGKey(key)
- if err != nil {
- t.Fatal(err)
- }
-
- // check if Artifact Regitry key exist or not
- artifactRegistryKeyFound := false
- for _, e := range entityList {
- for key := range e.Identities {
- if strings.Contains(key, "Artifact Registry") {
- artifactRegistryKeyFound = true
- }
- }
+ tests := []struct {
+ name string
+ url string
+ wantKeys int
+ wantErr error
+ }{
+ {
+ name: "empty armored key, want nil error",
+ url: srvURL + "/empty_armored",
+ wantKeys: 0,
+ wantErr: nil,
+ },
+ {
+ name: "valid binary key, want nil error",
+ url: srvURL + "/valid_binary",
+ wantKeys: 1,
+ wantErr: nil,
+ },
+ {
+ name: "valid armored key, want nil error",
+ url: srvURL + "/valid_armored",
+ wantKeys: 1,
+ wantErr: nil,
+ },
+ {
+ name: "invalid data, want invalid data error",
+ url: srvURL + "/invalid",
+ wantKeys: 0,
+ wantErr: openpgp_errors.StructuralError("tag byte does
not have MSB set"),
+ },
+ {
+ name: "invalid binary key, want unexpected EOF
error",
+ url: srvURL + "/binary",
+ wantKeys: 0,
+ wantErr: io.ErrUnexpectedEOF,
+ },
+ {
+ name: "large key, want too large error",
+ url: srvURL + "/large",
+ wantKeys: 0,
+ wantErr: errors.New("key size of 2000000 too large"),
+ },
+ {
+ name: "invalid url, want parse error",
+ url: "http://invalid:url",
+ wantKeys: 0,
+ wantErr: &url.Error{
+ Op: "parse",
+ URL: "http://invalid:url",
+ Err: errors.New(`invalid port ":url" after
host`),
+ },
+ },
}
- if !artifactRegistryKeyFound {
- t.Errorf("Expected to find Artifact Registry key in Google
Cloud Public GPG key, but its missed.")
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ gotKeys, gotErr := getAptGPGKey(tt.url)
+ utiltest.AssertErrorMatch(t, gotErr, tt.wantErr)
+ utiltest.AssertEquals(t, len(gotKeys), tt.wantKeys)
+ })
}
}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/osconfig-20260615.01/util/mocks/mock_agent_endpoint_beta_client.go
new/osconfig-20260624.01/util/mocks/mock_agent_endpoint_beta_client.go
--- old/osconfig-20260615.01/util/mocks/mock_agent_endpoint_beta_client.go
1970-01-01 01:00:00.000000000 +0100
+++ new/osconfig-20260624.01/util/mocks/mock_agent_endpoint_beta_client.go
2026-06-24 17:10:57.000000000 +0200
@@ -0,0 +1,85 @@
+// Copyright 2026 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by MockGen. DO NOT EDIT.
+// Source: cloud.google.com/go/osconfig/agentendpoint (interfaces:
agentEndpointBetaClient)
+
+// Package utilmocks is a generated GoMock package.
+package utilmocks
+
+import (
+ context "context"
+ reflect "reflect"
+
+ agentendpointpb
"cloud.google.com/go/osconfig/agentendpoint/apiv1beta/agentendpointpb"
+ gomock "github.com/golang/mock/gomock"
+ gax "github.com/googleapis/gax-go/v2"
+)
+
+// MockAgentEndpointBetaClient is a mock of agentEndpointBetaClient interface.
+type MockAgentEndpointBetaClient struct {
+ ctrl *gomock.Controller
+ recorder *MockAgentEndpointBetaClientMockRecorder
+}
+
+// MockAgentEndpointBetaClientMockRecorder is the mock recorder for
MockAgentEndpointBetaClient.
+type MockAgentEndpointBetaClientMockRecorder struct {
+ mock *MockAgentEndpointBetaClient
+}
+
+// NewMockAgentEndpointBetaClient creates a new mock instance.
+func NewMockAgentEndpointBetaClient(ctrl *gomock.Controller)
*MockAgentEndpointBetaClient {
+ mock := &MockAgentEndpointBetaClient{ctrl: ctrl}
+ mock.recorder = &MockAgentEndpointBetaClientMockRecorder{mock}
+ return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockAgentEndpointBetaClient) EXPECT()
*MockAgentEndpointBetaClientMockRecorder {
+ return m.recorder
+}
+
+// Close mocks base method.
+func (m *MockAgentEndpointBetaClient) Close() error {
+ m.ctrl.T.Helper()
+ ret := m.ctrl.Call(m, "Close")
+ ret0, _ := ret[0].(error)
+ return ret0
+}
+
+// Close indicates an expected call of Close.
+func (mr *MockAgentEndpointBetaClientMockRecorder) Close() *gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close",
reflect.TypeOf((*MockAgentEndpointBetaClient)(nil).Close))
+}
+
+// LookupEffectiveGuestPolicy mocks base method.
+func (m *MockAgentEndpointBetaClient) LookupEffectiveGuestPolicy(arg0
context.Context, arg1 *agentendpointpb.LookupEffectiveGuestPolicyRequest, arg2
...gax.CallOption) (*agentendpointpb.EffectiveGuestPolicy, error) {
+ m.ctrl.T.Helper()
+ varargs := []interface{}{arg0, arg1}
+ for _, a := range arg2 {
+ varargs = append(varargs, a)
+ }
+ ret := m.ctrl.Call(m, "LookupEffectiveGuestPolicy", varargs...)
+ ret0, _ := ret[0].(*agentendpointpb.EffectiveGuestPolicy)
+ ret1, _ := ret[1].(error)
+ return ret0, ret1
+}
+
+// LookupEffectiveGuestPolicy indicates an expected call of
LookupEffectiveGuestPolicy.
+func (mr *MockAgentEndpointBetaClientMockRecorder)
LookupEffectiveGuestPolicy(arg0, arg1 interface{}, arg2 ...interface{})
*gomock.Call {
+ mr.mock.ctrl.T.Helper()
+ varargs := append([]interface{}{arg0, arg1}, arg2...)
+ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock,
"LookupEffectiveGuestPolicy",
reflect.TypeOf((*MockAgentEndpointBetaClient)(nil).LookupEffectiveGuestPolicy),
varargs...)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/osconfig-20260615.01/util/utiltest/utiltest.go
new/osconfig-20260624.01/util/utiltest/utiltest.go
--- old/osconfig-20260615.01/util/utiltest/utiltest.go 2026-06-15
22:34:32.000000000 +0200
+++ new/osconfig-20260624.01/util/utiltest/utiltest.go 2026-06-24
17:10:57.000000000 +0200
@@ -14,6 +14,7 @@
"github.com/golang/mock/gomock"
"github.com/google/go-cmp/cmp"
"github.com/kr/pretty"
+ "google.golang.org/protobuf/testing/protocmp"
)
// AssertFormatMatch verifies that the got matches the wantFormat regular
expression.
@@ -113,7 +114,7 @@
// AssertEquals checks if got and want are deeply equal. If not, it fails the
test.
func AssertEquals(t *testing.T, got interface{}, want interface{}) {
t.Helper()
- if diff := cmp.Diff(want, got); diff != "" {
+ if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
t.Errorf("got != want (-want +got):\n%s", diff)
}
}
++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/google-osconfig-agent/vendor.tar.gz
/work/SRC/openSUSE:Factory/.google-osconfig-agent.new.11887/vendor.tar.gz
differ: char 14, line 1