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 2022-04-14 17:25:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/google-osconfig-agent (Old)
 and      /work/SRC/openSUSE:Factory/.google-osconfig-agent.new.1941 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "google-osconfig-agent"

Thu Apr 14 17:25:50 2022 rev:12 rq:970129 version:20220314.01

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/google-osconfig-agent/google-osconfig-agent.changes  
    2022-02-14 22:37:18.125590517 +0100
+++ 
/work/SRC/openSUSE:Factory/.google-osconfig-agent.new.1941/google-osconfig-agent.changes
    2022-04-14 17:26:27.451332959 +0200
@@ -1,0 +2,21 @@
+Thu Mar 17 08:49:05 UTC 2022 - John Paul Adrian Glaubitz 
<[email protected]>
+
+- Update to version 20220314.01
+  * Support COS on arm64 (#426)
+- from version 20220314.00
+  * Fix previous PR: exec.CommandContext cannot be reused (#425)
+- from version 20220304.00
+  * Update the error message when an exec task is run on Windows
+    without an interpreter (#423)
+  * Fix string that apt-get returns when requiring downgrade (#422)
+  * e2e_tests: fix patch test rerun (#421)
+  * Add --allow-downgrades flag to apt-get calls when it
+    fails because of wanting to downgrade a package (#418)
+  * Create e2e test that runs apt-get in a state that makes
+    it downgrade a package (#420)
+  * e2e_tests: update OS targets, adjust retries (#419)
+  * Create change_group.yaml (#416)
+- from version 20220215.00
+  * Add regex support to package exclusion in OS Patch (#415)
+
+-------------------------------------------------------------------

Old:
----
  osconfig-20220209.00.tar.gz

New:
----
  osconfig-20220314.01.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ google-osconfig-agent.spec ++++++
--- /var/tmp/diff_new_pack.IfmB0v/_old  2022-04-14 17:26:28.439334114 +0200
+++ /var/tmp/diff_new_pack.IfmB0v/_new  2022-04-14 17:26:28.439334114 +0200
@@ -24,7 +24,7 @@
 %global import_path     %{provider_prefix}
 
 Name:           google-osconfig-agent
-Version:        20220209.00
+Version:        20220314.01
 Release:        0
 Summary:        Google Cloud Guest Agent
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.IfmB0v/_old  2022-04-14 17:26:28.471334152 +0200
+++ /var/tmp/diff_new_pack.IfmB0v/_new  2022-04-14 17:26:28.475334156 +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">20220209.00</param>
-    <param name="revision">20220209.00</param>
+    <param name="versionformat">20220314.01</param>
+    <param name="revision">20220314.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-20220209.00.tar.gz</param>
+    <param name="archive">osconfig-20220314.01.tar.gz</param>
   </service>
 </services>
 

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.IfmB0v/_old  2022-04-14 17:26:28.491334175 +0200
+++ /var/tmp/diff_new_pack.IfmB0v/_new  2022-04-14 17:26:28.491334175 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/GoogleCloudPlatform/osconfig</param>
-              <param 
name="changesrevision">523d8645eeee2a36394f15ad8588629bebde3c2e</param></service></servicedata>
+              <param 
name="changesrevision">918d1031688a85121704ef568bf8026e8b647b32</param></service></servicedata>
 (No newline at EOF)
 

++++++ osconfig-20220209.00.tar.gz -> osconfig-20220314.01.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/agentendpoint/exec_task.go 
new/osconfig-20220314.01/agentendpoint/exec_task.go
--- old/osconfig-20220209.00/agentendpoint/exec_task.go 2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/agentendpoint/exec_task.go 2022-03-14 
19:59:56.000000000 +0100
@@ -45,7 +45,7 @@
        goos = runtime.GOOS
 
        errLinuxPowerShell = errors.New("interpreter POWERSHELL cannot be used 
on non-Windows system")
-       errWinNoInt        = fmt.Errorf("interpreter must be specified for a 
Windows system")
+       errWinNoInt        = fmt.Errorf("an interpreter value of `SHELL` or 
`POWERSHELL` must be set for files ran on Windows systems")
 )
 
 func init() {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/agentendpoint/patch_linux.go 
new/osconfig-20220314.01/agentendpoint/patch_linux.go
--- old/osconfig-20220209.00/agentendpoint/patch_linux.go       2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/agentendpoint/patch_linux.go       2022-03-14 
19:59:56.000000000 +0100
@@ -17,6 +17,7 @@
 import (
        "context"
        "errors"
+       "regexp"
        "strings"
        "time"
 
@@ -33,9 +34,13 @@
        const retryPeriod = 3 * time.Minute
        // Check for both apt-get and dpkg-query to give us a clean signal.
        if packages.AptExists && packages.DpkgQueryExists {
+               excludes, err := 
convertInputToExcludes(r.Task.GetPatchConfig().GetApt().GetExcludes())
+               if err != nil {
+                       return err
+               }
                opts := []ospatch.AptGetUpgradeOption{
                        ospatch.AptGetDryRun(r.Task.GetDryRun()),
-                       
ospatch.AptGetExcludes(r.Task.GetPatchConfig().GetApt().GetExcludes()),
+                       ospatch.AptGetExcludes(excludes),
                        
ospatch.AptGetExclusivePackages(r.Task.GetPatchConfig().GetApt().GetExclusivePackages()),
                }
                switch r.Task.GetPatchConfig().GetApt().GetType() {
@@ -48,10 +53,14 @@
                }
        }
        if packages.YumExists && packages.RPMQueryExists {
+               excludes, err := 
convertInputToExcludes(r.Task.GetPatchConfig().GetYum().GetExcludes())
+               if err != nil {
+                       return err
+               }
                opts := []ospatch.YumUpdateOption{
                        
ospatch.YumUpdateSecurity(r.Task.GetPatchConfig().GetYum().GetSecurity()),
                        
ospatch.YumUpdateMinimal(r.Task.GetPatchConfig().GetYum().GetMinimal()),
-                       
ospatch.YumUpdateExcludes(r.Task.GetPatchConfig().GetYum().GetExcludes()),
+                       ospatch.YumUpdateExcludes(excludes),
                        
ospatch.YumExclusivePackages(r.Task.GetPatchConfig().GetYum().GetExclusivePackages()),
                        ospatch.YumDryRun(r.Task.GetDryRun()),
                }
@@ -61,12 +70,16 @@
                }
        }
        if packages.ZypperExists && packages.RPMQueryExists {
+               excludes, err := 
convertInputToExcludes(r.Task.GetPatchConfig().GetZypper().GetExcludes())
+               if err != nil {
+                       return err
+               }
                opts := []ospatch.ZypperPatchOption{
                        
ospatch.ZypperPatchCategories(r.Task.GetPatchConfig().GetZypper().GetCategories()),
                        
ospatch.ZypperPatchSeverities(r.Task.GetPatchConfig().GetZypper().GetSeverities()),
                        
ospatch.ZypperUpdateWithUpdate(r.Task.GetPatchConfig().GetZypper().GetWithUpdate()),
                        
ospatch.ZypperUpdateWithOptional(r.Task.GetPatchConfig().GetZypper().GetWithOptional()),
-                       
ospatch.ZypperUpdateWithExcludes(r.Task.GetPatchConfig().GetZypper().GetExcludes()),
+                       ospatch.ZypperUpdateWithExcludes(excludes),
                        
ospatch.ZypperUpdateWithExclusivePatches(r.Task.GetPatchConfig().GetZypper().GetExclusivePatches()),
                        ospatch.ZypperUpdateDryrun(r.Task.GetDryRun()),
                }
@@ -80,3 +93,27 @@
        }
        return errors.New(strings.Join(errs, ",\n"))
 }
+
+func convertInputToExcludes(input []string) ([]*ospatch.Exclude, error) {
+       var output []*ospatch.Exclude
+       for _, s := range input {
+               if len(s) >= 2 && (s)[0] == '/' && s[len(s)-1] == '/' {
+                       exclude, err := regexExcludeFromString(s[1 : len(s)-1])
+                       if err != nil {
+                               return nil, err
+                       }
+                       output = append(output, exclude)
+               } else {
+                       output = append(output, ospatch.CreateStringExclude(&s))
+               }
+       }
+       return output, nil
+}
+
+func regexExcludeFromString(s string) (*ospatch.Exclude, error) {
+       compile, err := regexp.Compile(s)
+       if err != nil {
+               return nil, err
+       }
+       return ospatch.CreateRegexExclude(compile), nil
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osconfig-20220209.00/agentendpoint/patch_linux_test.go 
new/osconfig-20220314.01/agentendpoint/patch_linux_test.go
--- old/osconfig-20220209.00/agentendpoint/patch_linux_test.go  1970-01-01 
01:00:00.000000000 +0100
+++ new/osconfig-20220314.01/agentendpoint/patch_linux_test.go  2022-03-14 
19:59:56.000000000 +0100
@@ -0,0 +1,55 @@
+//  Copyright 2022 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 (
+       "reflect"
+       "regexp"
+       "testing"
+
+       "github.com/GoogleCloudPlatform/osconfig/ospatch"
+)
+
+func TestExcludeConversion(t *testing.T) {
+       strictString := "PackageName"
+       regex, _ := regexp.Compile("PackageName")
+       emptyRegex, _ := regexp.Compile("")
+       slashString := "/"
+       emptyString := ""
+
+       tests := []struct {
+               name  string
+               input []string
+               want  []*ospatch.Exclude
+       }{
+               {name: "StrictStringConversion", input: 
[]string{"PackageName"}, want: 
[]*ospatch.Exclude{ospatch.CreateStringExclude(&strictString)}},
+               {name: "RegexConversion", input: []string{"/PackageName/"}, 
want: []*ospatch.Exclude{ospatch.CreateRegexExclude(regex)}},
+               {name: "CornerCaseRegex", input: []string{"//"}, want: 
[]*ospatch.Exclude{ospatch.CreateRegexExclude(emptyRegex)}},
+               {name: "CornerCaseStrictString", input: []string{"/"}, want: 
[]*ospatch.Exclude{ospatch.CreateStringExclude(&slashString)}},
+               {name: "CornerCaseEmptyString", input: []string{""}, want: 
[]*ospatch.Exclude{ospatch.CreateStringExclude(&emptyString)}},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       excludes, err := convertInputToExcludes(tt.input)
+                       if err != nil {
+                               t.Errorf("err = %v, want %v", err, nil)
+                       }
+                       if !reflect.DeepEqual(excludes, tt.want) {
+                               t.Errorf("convertInputToExcludes() = %v, want = 
%v", excludes, tt.want)
+                       }
+               })
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osconfig-20220209.00/e2e_tests/test_suites/inventory/inventory.go 
new/osconfig-20220314.01/e2e_tests/test_suites/inventory/inventory.go
--- old/osconfig-20220209.00/e2e_tests/test_suites/inventory/inventory.go       
2022-02-09 02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/e2e_tests/test_suites/inventory/inventory.go       
2022-03-14 19:59:56.000000000 +0100
@@ -132,7 +132,7 @@
        return ga
 }
 
-func runHostnameTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup, testCase *junitxml.TestCase) {
+func runHostnameTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup) error {
        var hostname string
        for _, item := range ga {
                if item.Key == "Hostname" {
@@ -143,16 +143,16 @@
 
        if hostname == "" {
                s, _ := json.MarshalIndent(ga, "", "  ")
-               testCase.WriteFailure("Hostname not found in guestInventory: 
%s", s)
-               return
+               return fmt.Errorf("Hostname not found in guestInventory: %s", s)
        }
 
        if hostname != testSetup.hostname {
-               testCase.WriteFailure("Hostname does not match expectation: 
got: %q, want: %q", hostname, testSetup.hostname)
+               return fmt.Errorf("Hostname does not match expectation: got: 
%q, want: %q", hostname, testSetup.hostname)
        }
+       return nil
 }
 
-func runShortNameTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup, testCase *junitxml.TestCase) {
+func runShortNameTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup) error {
        var shortName string
        for _, item := range ga {
                if item.Key == "ShortName" {
@@ -163,16 +163,16 @@
 
        if shortName == "" {
                s, _ := json.MarshalIndent(ga, "", "  ")
-               testCase.WriteFailure("ShortName not found in guestInventory: 
%s", s)
-               return
+               return fmt.Errorf("ShortName not found in guestInventory: %s", 
s)
        }
 
        if shortName != testSetup.shortName {
-               testCase.WriteFailure("ShortName does not match expectation: 
got: %q, want: %q", shortName, testSetup.shortName)
+               return fmt.Errorf("ShortName does not match expectation: got: 
%q, want: %q", shortName, testSetup.shortName)
        }
+       return nil
 }
 
-func runPackagesTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup, testCase *junitxml.TestCase) {
+func runPackagesTest(ga []*apiBeta.GuestAttributesEntry, testSetup 
*inventoryTestSetup) error {
        var packagesEncoded string
        for _, item := range ga {
                if item.Key == "InstalledPackages" {
@@ -183,136 +183,104 @@
 
        if packagesEncoded == "" {
                s, _ := json.MarshalIndent(ga, "", "  ")
-               testCase.WriteFailure("InstalledPackages not found in 
guestInventory: %s", s)
-               return
+               return fmt.Errorf("InstalledPackages not found in 
guestInventory: %s", s)
        }
 
        decoded, err := base64.StdEncoding.DecodeString(packagesEncoded)
        if err != nil {
-               testCase.WriteFailure(err.Error())
-               return
+               return err
        }
 
        zr, err := gzip.NewReader(bytes.NewReader(decoded))
        if err != nil {
-               testCase.WriteFailure(err.Error())
-               return
+               return err
        }
        defer zr.Close()
 
        var buf bytes.Buffer
        if _, err := io.Copy(&buf, zr); err != nil {
-               testCase.WriteFailure(err.Error())
-               return
+               return err
        }
 
        var pkgs packages.Packages
        if err := json.Unmarshal(buf.Bytes(), &pkgs); err != nil {
-               testCase.WriteFailure(err.Error())
-               return
+               return err
        }
 
        for _, pt := range testSetup.packageType {
                switch pt {
                case "googet":
                        if len(pkgs.GooGet) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "deb":
                        if len(pkgs.Deb) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "rpm":
                        if len(pkgs.Rpm) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "pip":
                        if len(pkgs.Pip) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "gem":
                        if len(pkgs.Gem) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "wua":
                        if len(pkgs.WUA) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                case "qfe":
                        if len(pkgs.QFE) < 1 {
-                               testCase.WriteFailure("No packages exported in 
InstalledPackages for %q", pt)
-                               return
+                               return fmt.Errorf("no packages exported in 
InstalledPackages for %q", pt)
                        }
                }
        }
+       return nil
 }
 
 func inventoryTestCase(ctx context.Context, testSetup *inventoryTestSetup, 
tests chan *junitxml.TestCase, wg *sync.WaitGroup, logger *log.Logger, regex 
*regexp.Regexp) {
        defer wg.Done()
 
        var logwg sync.WaitGroup
-       gatherInventoryTest := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[Gather inventory] [%s]", testSetup.testName))
-       hostnameTest := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[Check 
Hostname] [%s]", testSetup.testName))
-       shortNameTest := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[Check ShortName] [%s]", testSetup.testName))
-       packageTest := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[Check 
InstalledPackages] [%s]", testSetup.testName))
-
-       if gatherInventoryTest.FilterTestCase(regex) {
-               gatherInventoryTest.Finish(tests)
-
-               hostnameTest.WriteSkipped("Setup skipped")
-               hostnameTest.Finish(tests)
-               shortNameTest.WriteSkipped("Setup skipped")
-               hostnameTest.Finish(tests)
-               packageTest.WriteSkipped("Setup skipped")
-               packageTest.Finish(tests)
+       inventoryTest := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[Guest Attributes inventory] [%s]", testSetup.testName))
+
+       if inventoryTest.FilterTestCase(regex) {
+               inventoryTest.Finish(tests)
                return
        }
 
-       logger.Printf("Running TestCase %q", gatherInventoryTest.Name)
-       ga := runGatherInventoryTest(ctx, testSetup, gatherInventoryTest, 
&logwg)
-       gatherInventoryTest.Finish(tests)
-       logger.Printf("TestCase %q finished", gatherInventoryTest.Name)
-       if gatherInventoryTest.Failure != nil {
-               rerunTC := junitxml.NewTestCase(testSuiteName, 
strings.TrimPrefix(gatherInventoryTest.Name, fmt.Sprintf("[%s] ", 
testSuiteName)))
+       logger.Printf("Running TestCase %q", inventoryTest.Name)
+       ga := runGatherInventoryTest(ctx, testSetup, inventoryTest, &logwg)
+       if inventoryTest.Failure != nil {
+               rerunTC := junitxml.NewTestCase(testSuiteName, 
strings.TrimPrefix(inventoryTest.Name, fmt.Sprintf("[%s] ", testSuiteName)))
                logger.Printf("Rerunning TestCase %q", rerunTC.Name)
                ga = runGatherInventoryTest(ctx, testSetup, rerunTC, &logwg)
-               rerunTC.Finish(tests)
-               logger.Printf("TestCase %q finished in %fs", rerunTC.Name, 
rerunTC.Time)
                if rerunTC.Failure != nil {
-                       hostnameTest.WriteFailure("Setup Failure")
-                       hostnameTest.Finish(tests)
-                       shortNameTest.WriteFailure("Setup Failure")
-                       shortNameTest.Finish(tests)
-                       packageTest.WriteFailure("Setup Failure")
-                       packageTest.Finish(tests)
+                       logger.Printf("TestCase %q finished in %fs", 
rerunTC.Name, rerunTC.Time)
+                       rerunTC.Finish(tests)
                        return
                }
        }
 
-       for tc, f := range 
map[*junitxml.TestCase]func([]*apiBeta.GuestAttributesEntry, 
*inventoryTestSetup, *junitxml.TestCase){
-               hostnameTest:  runHostnameTest,
-               shortNameTest: runShortNameTest,
-               packageTest:   runPackagesTest,
-       } {
-               // Skip packages test for cos as it is not currently supported.
-               if strings.Contains(tc.Name, "cos") && 
strings.Contains(tc.Name, "Packages") {
-                       tc.WriteSkipped("Inventory Packages not currently 
supported on COS")
-                       tc.Finish(tests)
-               } else if tc.FilterTestCase(regex) {
-                       tc.Finish(tests)
-               } else {
-                       logger.Printf("Running TestCase %q", tc.Name)
-                       f(ga, testSetup, tc)
-                       tc.Finish(tests)
-                       logger.Printf("TestCase %q finished in %fs", tc.Name, 
tc.Time)
+       if err := runHostnameTest(ga, testSetup); err != nil {
+               inventoryTest.WriteFailure("Error checking hostname: %v", err)
+       }
+       if err := runShortNameTest(ga, testSetup); err != nil {
+               inventoryTest.WriteFailure("Error checking shortname: %v", err)
+       }
+
+       // Skip packages test for cos as it is not currently supported.
+       if !strings.Contains(inventoryTest.Name, "cos") {
+               if err := runPackagesTest(ga, testSetup); err != nil {
+                       inventoryTest.WriteFailure("Error checking packages: 
%v", err)
                }
        }
-       logwg.Wait()
 
+       logwg.Wait()
+       inventoryTest.Finish(tests)
+       logger.Printf("TestCase %q finished", inventoryTest.Name)
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osconfig-20220209.00/e2e_tests/test_suites/patch/patch.go 
new/osconfig-20220314.01/e2e_tests/test_suites/patch/patch.go
--- old/osconfig-20220209.00/e2e_tests/test_suites/patch/patch.go       
2022-02-09 02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/e2e_tests/test_suites/patch/patch.go       
2022-03-14 19:59:56.000000000 +0100
@@ -23,6 +23,7 @@
        "path"
        "regexp"
        "strconv"
+       "strings"
        "sync"
        "time"
 
@@ -69,7 +70,7 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[Execute 
PatchJob] [%s]", s.testName))
-               f := func() { runExecutePatchJobTest(ctx, tc, s, nil) }
+               f := func(tc *junitxml.TestCase) { runExecutePatchJobTest(ctx, 
tc, s, nil) }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
        // TODO: remove this hack and setup specific test suites for each test 
type.
@@ -82,7 +83,7 @@
                        tc := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[PatchJob triggers reboot] [%s]", s.testName))
                        pc := &osconfigpb.PatchConfig{Apt: 
&osconfigpb.AptSettings{Type: osconfigpb.AptSettings_DIST}}
                        shouldReboot := true
-                       f := func() {
+                       f := func(tc *junitxml.TestCase) {
                                runRebootPatchTest(ctx, tc, s, pc, shouldReboot)
                        }
                        go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
@@ -95,7 +96,7 @@
                        tc := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[PatchJob does not reboot] [%s]", s.testName))
                        pc := &osconfigpb.PatchConfig{RebootConfig: 
osconfigpb.PatchConfig_NEVER, Apt: &osconfigpb.AptSettings{Type: 
osconfigpb.AptSettings_DIST}}
                        shouldReboot := false
-                       f := func() { runRebootPatchTest(ctx, tc, s, pc, 
shouldReboot) }
+                       f := func(tc *junitxml.TestCase) { 
runRebootPatchTest(ctx, tc, s, pc, shouldReboot) }
                        go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
                }
        }
@@ -105,7 +106,7 @@
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[PatchJob runs pre-step and post-step] [%s]", s.testName))
                pc := patchConfigWithPrePostSteps()
-               f := func() { runExecutePatchJobTest(ctx, tc, s, pc) }
+               f := func(tc *junitxml.TestCase) { runExecutePatchJobTest(ctx, 
tc, s, pc) }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
        // Test APT specific functionality, this just tests that using these 
settings doesn't break anything.
@@ -113,8 +114,8 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[APT 
dist-upgrade, excludes] [%s]", s.testName))
-               f := func() {
-                       runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Apt: &osconfigpb.AptSettings{Type: 
osconfigpb.AptSettings_DIST, Excludes: []string{"pkg1"}}})
+               f := func(tc *junitxml.TestCase) {
+                       runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Apt: &osconfigpb.AptSettings{Type: 
osconfigpb.AptSettings_DIST, Excludes: []string{"pkg1", "/pkg2/"}}})
                }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
@@ -123,18 +124,28 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[APT 
dist-upgrade, exclusive packages] [%s]", s.testName))
-               f := func() {
+               f := func(tc *junitxml.TestCase) {
                        runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Apt: &osconfigpb.AptSettings{Type: 
osconfigpb.AptSettings_DIST, ExclusivePackages: []string{"pkg1"}}})
                }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
+       // Test that apt-get patch works even when a package needs to be 
downgraded
+       for _, setup := range aptDowngradeImageTestSetup() {
+               wg.Add(1)
+               s := setup
+               tc := junitxml.NewTestCase(testSuiteName, 
fmt.Sprintf("[PatchJob apt-get doesn't fail on downgrades] [%s]", s.testName))
+               f := func(tc *junitxml.TestCase) {
+                       runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Apt: &osconfigpb.AptSettings{Type: 
osconfigpb.AptSettings_DIST}})
+               }
+               go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
+       }
        // Test YUM specific functionality, this just tests that using these 
settings doesn't break anything.
        for _, setup := range yumHeadImageTestSetup() {
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[YUM 
security, minimal and excludes] [%s]", s.testName))
-               f := func() {
-                       runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Yum: &osconfigpb.YumSettings{Security: true, Minimal: 
true, Excludes: []string{"pkg1", "pkg2"}}})
+               f := func(tc *junitxml.TestCase) {
+                       runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Yum: &osconfigpb.YumSettings{Security: true, Minimal: 
true, Excludes: []string{"pkg1", "pkg2", "/pkg3/"}}})
                }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
@@ -143,7 +154,7 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[YUM 
exclusive patches] [%s]", s.testName))
-               f := func() {
+               f := func(tc *junitxml.TestCase) {
                        runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{Yum: &osconfigpb.YumSettings{ExclusivePackages: 
[]string{"pkg1", "pk3"}}})
                }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
@@ -153,9 +164,9 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[Zypper 
excludes, WithOptional, WithUpdate, Categories and Severities] [%s]", 
s.testName))
-               f := func() {
+               f := func(tc *junitxml.TestCase) {
                        runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{
-                               Zypper: &osconfigpb.ZypperSettings{Excludes: 
[]string{"patch-1"}, WithOptional: true, WithUpdate: true, Categories: 
[]string{"security", "recommended", "feature"}, Severities: 
[]string{"critical", "important", "moderate", "low"}}})
+                               Zypper: &osconfigpb.ZypperSettings{Excludes: 
[]string{"patch-1", "/patch-2/"}, WithOptional: true, WithUpdate: true, 
Categories: []string{"security", "recommended", "feature"}, Severities: 
[]string{"critical", "important", "moderate", "low"}}})
                }
                go runTestCase(tc, f, tests, &wg, logger, testCaseRegex)
        }
@@ -165,7 +176,7 @@
                wg.Add(1)
                s := setup
                tc := junitxml.NewTestCase(testSuiteName, fmt.Sprintf("[Zypper 
exclusivePatches] [%s]", s.testName))
-               f := func() {
+               f := func(tc *junitxml.TestCase) {
                        runExecutePatchJobTest(ctx, tc, s, 
&osconfigpb.PatchConfig{
                                Zypper: 
&osconfigpb.ZypperSettings{ExclusivePatches: []string{"patch-1"}}}) // there 
should be no patch run
 
@@ -411,14 +422,25 @@
                state == osconfigpb.PatchJob_CANCELED
 }
 
-func runTestCase(tc *junitxml.TestCase, f func(), tests chan 
*junitxml.TestCase, wg *sync.WaitGroup, logger *log.Logger, regex 
*regexp.Regexp) {
+func runTestCase(tc *junitxml.TestCase, f func(tc *junitxml.TestCase), tests 
chan *junitxml.TestCase, wg *sync.WaitGroup, logger *log.Logger, regex 
*regexp.Regexp) {
        defer wg.Done()
 
        if tc.FilterTestCase(regex) {
                tc.Finish(tests)
        } else {
                logger.Printf("Running TestCase %q", tc.Name)
-               f()
+               f(tc)
+               if tc.Failure != nil {
+                       rerunTC := junitxml.NewTestCase(testSuiteName, 
strings.TrimPrefix(tc.Name, fmt.Sprintf("[%s] ", testSuiteName)))
+                       wg.Add(1)
+                       go func() {
+                               defer wg.Done()
+                               logger.Printf("Rerunning TestCase %q", 
rerunTC.Name)
+                               f(rerunTC)
+                               rerunTC.Finish(tests)
+                               logger.Printf("TestCase %q finished in %fs", 
rerunTC.Name, rerunTC.Time)
+                       }()
+               }
                tc.Finish(tests)
                logger.Printf("TestCase %q finished in %fs", tc.Name, tc.Time)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osconfig-20220209.00/e2e_tests/test_suites/patch/test_setup.go 
new/osconfig-20220314.01/e2e_tests/test_suites/patch/test_setup.go
--- old/osconfig-20220209.00/e2e_tests/test_suites/patch/test_setup.go  
2022-02-09 02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/e2e_tests/test_suites/patch/test_setup.go  
2022-03-14 19:59:56.000000000 +0100
@@ -80,6 +80,13 @@
 chmod +x ./linux_local_pre_patch_script.sh
 `
 
+       setUpDowngradeState = `
+echo 'deb [trusted=yes check-valid-until=no] 
http://snapshot.debian.org/archive/debian/20190801T025637Z/ buster main' >> 
/etc/apt/sources.list
+echo 'Package: sudo' >> /etc/apt/preferences
+echo 'Pin: version 1.8.27-1' >> /etc/apt/preferences
+echo 'Pin-priority: 9999' >> /etc/apt/preferences
+`
+
        enableOsconfig  = compute.BuildInstanceMetadataItem("enable-osconfig", 
"true")
        disableFeatures = 
compute.BuildInstanceMetadataItem("osconfig-disabled-features", 
"guestpolicies,osinventory")
 
@@ -102,6 +109,15 @@
                },
                machineType: "e2-medium",
        }
+       aptDowngradeSetup = &patchTestSetup{
+               assertTimeout: 10 * time.Minute,
+               metadata: []*computeApi.MetadataItems{
+                       compute.BuildInstanceMetadataItem("startup-script", 
linuxRecordBoot+utils.InstallOSConfigDeb()+linuxLocalPrePatchScript+setUpDowngradeState),
+                       enableOsconfig,
+                       disableFeatures,
+               },
+               machineType: "e2-medium",
+       }
        el6Setup = &patchTestSetup{
                assertTimeout: 15 * time.Minute,
                metadata: []*computeApi.MetadataItems{
@@ -187,6 +203,15 @@
        }
 
        return imageTestSetup(mapping)
+}
+
+func aptDowngradeImageTestSetup() []*patchTestSetup {
+       // This maps a specific patchTestSetup to test setup names and 
associated images.
+       mapping := map[*patchTestSetup]map[string]string{
+               aptDowngradeSetup: utils.DowngradeAptImages,
+       }
+
+       return imageTestSetup(mapping)
 }
 
 func yumHeadImageTestSetup() []*patchTestSetup {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/e2e_tests/utils/utils.go 
new/osconfig-20220314.01/e2e_tests/utils/utils.go
--- old/osconfig-20220209.00/e2e_tests/utils/utils.go   2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/e2e_tests/utils/utils.go   2022-03-14 
19:59:56.000000000 +0100
@@ -206,11 +206,17 @@
        return ""
 }
 
+// DowngradeAptImages is a single image that are used for testing downgrade 
case with apt-get
+var DowngradeAptImages = map[string]string{
+       "debian-cloud/debian-10": 
"projects/debian-cloud/global/images/family/debian-10",
+}
+
 // HeadAptImages is a map of names to image paths for public image families 
that use APT.
 var HeadAptImages = map[string]string{
        // Debian images.
        "debian-cloud/debian-9":  
"projects/debian-cloud/global/images/family/debian-9",
        "debian-cloud/debian-10": 
"projects/debian-cloud/global/images/family/debian-10",
+       "debian-cloud/debian-11": 
"projects/debian-cloud/global/images/family/debian-11",
 
        // Ubuntu images.
        "ubuntu-os-cloud/ubuntu-1804-lts": 
"projects/ubuntu-os-cloud/global/images/family/ubuntu-1804-lts",
@@ -304,8 +310,9 @@
        "windows-cloud/windows-2016-core":    
"projects/windows-cloud/global/images/family/windows-2016-core",
        "windows-cloud/windows-2019":         
"projects/windows-cloud/global/images/family/windows-2019",
        "windows-cloud/windows-2019-core":    
"projects/windows-cloud/global/images/family/windows-2019-core",
-       "windows-cloud/windows-2004-core":    
"projects/windows-cloud/global/images/family/windows-2004-core",
        "windows-cloud/windows-20h2-core":    
"projects/windows-cloud/global/images/family/windows-20h2-core",
+       "windows-cloud/windows-2022":         
"projects/windows-cloud/global/images/family/windows-2022",
+       "windows-cloud/windows-2022-core":    
"projects/windows-cloud/global/images/family/windows-2022-core",
 }
 
 // OldWindowsImages is a map of names to image paths for old Windows images.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/osconfig-20220209.00/examples/OSPolicyAssignments/gcloud/change_group.yaml 
new/osconfig-20220314.01/examples/OSPolicyAssignments/gcloud/change_group.yaml
--- 
old/osconfig-20220209.00/examples/OSPolicyAssignments/gcloud/change_group.yaml  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/osconfig-20220314.01/examples/OSPolicyAssignments/gcloud/change_group.yaml  
    2022-03-14 19:59:56.000000000 +0100
@@ -0,0 +1,26 @@
+# OS policy assignment that sets the /var/log/auth.log group to logaccess.
+osPolicies:
+ - id: log-access-policy
+   mode: ENFORCEMENT
+   resourceGroups:
+       resources:
+         - id: grant-log-access
+           exec:
+             validate:
+               # Checks if the group is logaccess. If yes, exits  with code 
100. If no,
+               # exits with code 101 and proceeds to the `enforce` step.
+               script:
+                 if stat -c '%G' /var/log/auth.log | grep -q 'logaccess'; then 
exit 100; else exit 101; fi
+               interpreter: SHELL
+             enforce:
+               # Changes the group to logaccess and exits with code 100.
+               script:
+                 chgrp logaccess /var/log/auth.log && exit 100
+instanceFilter:
+ inclusionLabels:
+   - labels:
+       vm: tutorial
+rollout:
+ disruptionBudget:
+   fixed: 10
+ minWaitDuration: 30s
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/apt_upgrade.go 
new/osconfig-20220314.01/ospatch/apt_upgrade.go
--- old/osconfig-20220209.00/ospatch/apt_upgrade.go     2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/apt_upgrade.go     2022-03-14 
19:59:56.000000000 +0100
@@ -24,7 +24,7 @@
 
 type aptGetUpgradeOpts struct {
        exclusivePackages []string
-       excludes          []string
+       excludes          []*Exclude
        upgradeType       packages.AptUpgradeType
        dryrun            bool
 }
@@ -40,7 +40,7 @@
 }
 
 // AptGetExcludes excludes these packages from upgrade.
-func AptGetExcludes(excludes []string) AptGetUpgradeOption {
+func AptGetExcludes(excludes []*Exclude) AptGetUpgradeOption {
        return func(args *aptGetUpgradeOpts) {
                args.excludes = excludes
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/exclude.go 
new/osconfig-20220314.01/ospatch/exclude.go
--- old/osconfig-20220209.00/ospatch/exclude.go 1970-01-01 01:00:00.000000000 
+0100
+++ new/osconfig-20220314.01/ospatch/exclude.go 2022-03-14 19:59:56.000000000 
+0100
@@ -0,0 +1,50 @@
+//  Copyright 2022 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 ospatch
+
+import (
+       "regexp"
+)
+
+// Exclude represents package exclude entry by a user
+type Exclude struct {
+       isRegexp     bool
+       regex        *regexp.Regexp
+       strictString *string
+}
+
+// CreateRegexExclude returns new Exclude struct that represents exclusion 
with regex
+func CreateRegexExclude(regex *regexp.Regexp) *Exclude {
+       return &Exclude{
+               isRegexp: true,
+               regex:    regex,
+       }
+}
+
+// CreateStringExclude returns new Exclude struct that represents exclusion 
with string
+func CreateStringExclude(strictString *string) *Exclude {
+       return &Exclude{
+               isRegexp:     false,
+               strictString: strictString,
+       }
+}
+
+// MatchesName returns if a package with a certain name matches Exclude struct 
and should be excluded
+func (exclude *Exclude) MatchesName(name *string) bool {
+       if exclude.isRegexp {
+               return exclude.regex.MatchString(*name)
+       }
+       return *exclude.strictString == *name
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/googet_update.go 
new/osconfig-20220314.01/ospatch/googet_update.go
--- old/osconfig-20220209.00/ospatch/googet_update.go   2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/googet_update.go   2022-03-14 
19:59:56.000000000 +0100
@@ -24,7 +24,7 @@
 
 type googetUpdateOpts struct {
        exclusivePackages []string
-       excludes          []string
+       excludes          []*Exclude
        dryrun            bool
 }
 
@@ -32,7 +32,7 @@
 type GooGetUpdateOption func(*googetUpdateOpts)
 
 // GooGetExcludes excludes these packages from upgrade.
-func GooGetExcludes(excludes []string) GooGetUpdateOption {
+func GooGetExcludes(excludes []*Exclude) GooGetUpdateOption {
        return func(args *googetUpdateOpts) {
                args.excludes = excludes
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/updates.go 
new/osconfig-20220314.01/ospatch/updates.go
--- old/osconfig-20220209.00/ospatch/updates.go 2022-02-09 02:56:07.000000000 
+0100
+++ new/osconfig-20220314.01/ospatch/updates.go 2022-03-14 19:59:56.000000000 
+0100
@@ -111,6 +111,15 @@
        return rpmRebootRequired(out, btime), nil
 }
 
+func shouldPackageBeExcluded(excludes []*Exclude, packageName *string) bool {
+       for _, exclude := range excludes {
+               if exclude.MatchesName(packageName) {
+                       return true
+               }
+       }
+       return false
+}
+
 func containsString(ss []string, c string) bool {
        for _, s := range ss {
                if s == c {
@@ -120,13 +129,13 @@
        return false
 }
 
-func filterPackages(pkgs []*packages.PkgInfo, exclusivePackages, excludes 
[]string) ([]*packages.PkgInfo, error) {
+func filterPackages(pkgs []*packages.PkgInfo, exclusivePackages []string, 
excludes []*Exclude) ([]*packages.PkgInfo, error) {
        if len(exclusivePackages) != 0 && len(excludes) != 0 {
                return nil, errors.New("exclusivePackages and excludes can not 
both be non 0")
        }
-       var fPkgs []*packages.PkgInfo
+       var fPkgs = []*packages.PkgInfo{}
        for _, pkg := range pkgs {
-               if containsString(excludes, pkg.Name) {
+               if shouldPackageBeExcluded(excludes, &pkg.Name) {
                        continue
                }
                if exclusivePackages == nil || 
containsString(exclusivePackages, pkg.Name) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/updates_test.go 
new/osconfig-20220314.01/ospatch/updates_test.go
--- old/osconfig-20220209.00/ospatch/updates_test.go    2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/updates_test.go    2022-03-14 
19:59:56.000000000 +0100
@@ -17,7 +17,11 @@
 import (
        "io/ioutil"
        "os"
+       "reflect"
+       "regexp"
        "testing"
+
+       "github.com/GoogleCloudPlatform/osconfig/packages"
 )
 
 func TestGetBtime(t *testing.T) {
@@ -83,4 +87,33 @@
                        }
                })
        }
+}
+
+func TestFilterPackages(t *testing.T) {
+       pkg := packages.PkgInfo{Name: "NameOfThePackage"}
+       strictString := "NameOfThePackage"
+       regex, _ := regexp.Compile("^NameO[e-g]ThePackage$")
+       missingRegex, _ := regexp.Compile("^NameO[e-g]ThePackag$")
+       tests := []struct {
+               name    string
+               pkgs    []*packages.PkgInfo
+               exludes []*Exclude
+               want    []*packages.PkgInfo
+       }{
+               {name: "StrictStringFiltering", pkgs: 
[]*packages.PkgInfo{&pkg}, exludes: 
[]*Exclude{CreateStringExclude(&strictString)}, want: []*packages.PkgInfo{}},
+               {name: "RegexpFiltering", pkgs: []*packages.PkgInfo{&pkg}, 
exludes: []*Exclude{CreateRegexExclude(regex)}, want: []*packages.PkgInfo{}},
+               {name: "MissedFilter", pkgs: []*packages.PkgInfo{&pkg}, 
exludes: []*Exclude{CreateRegexExclude(missingRegex)}, want: 
[]*packages.PkgInfo{&pkg}},
+       }
+
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got, err := filterPackages(tt.pkgs, nil, tt.exludes)
+                       if err != nil {
+                               t.Errorf("err = %v, want %v", err, nil)
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("filterPackages() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/yum_update.go 
new/osconfig-20220314.01/ospatch/yum_update.go
--- old/osconfig-20220209.00/ospatch/yum_update.go      2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/yum_update.go      2022-03-14 
19:59:56.000000000 +0100
@@ -31,7 +31,7 @@
 
 type yumUpdateOpts struct {
        exclusivePackages []string
-       excludes          []string
+       excludes          []*Exclude
        security          bool
        minimal           bool
        dryrun            bool
@@ -58,7 +58,7 @@
 
 // YumUpdateExcludes returns a YumUpdateOption that specifies what packages to 
add to
 // the --exclude flag.
-func YumUpdateExcludes(excludes []string) YumUpdateOption {
+func YumUpdateExcludes(excludes []*Exclude) YumUpdateOption {
        return func(args *yumUpdateOpts) {
                args.excludes = excludes
        }
@@ -90,14 +90,14 @@
                opt(yumOpts)
        }
 
-       pkgs, err := packages.YumUpdates(ctx, 
packages.YumUpdateMinimal(yumOpts.minimal), 
packages.YumUpdateSecurity(yumOpts.security), 
packages.YumExcludes(yumOpts.excludes))
+       pkgs, err := packages.YumUpdates(ctx, 
packages.YumUpdateMinimal(yumOpts.minimal), 
packages.YumUpdateSecurity(yumOpts.security))
        if err != nil {
                return err
        }
 
        // Yum excludes are already excluded while listing yumUpdates, so we 
send
        // and empty list.
-       fPkgs, err := filterPackages(pkgs, yumOpts.exclusivePackages, 
[]string{})
+       fPkgs, err := filterPackages(pkgs, yumOpts.exclusivePackages, 
yumOpts.excludes)
        if err != nil {
                return err
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/zypper_patch.go 
new/osconfig-20220314.01/ospatch/zypper_patch.go
--- old/osconfig-20220209.00/ospatch/zypper_patch.go    2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/zypper_patch.go    2022-03-14 
19:59:56.000000000 +0100
@@ -31,7 +31,7 @@
 type zypperPatchOpts struct {
        categories       []string
        severities       []string
-       excludes         []string
+       excludes         []*Exclude
        exclusivePatches []string
        withOptional     bool
        withUpdate       bool
@@ -75,7 +75,7 @@
 
 // ZypperUpdateWithExcludes returns a ZypperUpdateOption that specifies
 // list of packages to be excluded from update
-func ZypperUpdateWithExcludes(excludes []string) ZypperPatchOption {
+func ZypperUpdateWithExcludes(excludes []*Exclude) ZypperPatchOption {
        return func(args *zypperPatchOpts) {
                args.excludes = excludes
        }
@@ -182,7 +182,7 @@
        return err
 }
 
-func runFilter(patches []*packages.ZypperPatch, exclusivePatches, excludes 
[]string, pkgUpdates []*packages.PkgInfo, pkgToPatchesMap map[string][]string, 
withUpdate bool) ([]*packages.ZypperPatch, []*packages.PkgInfo, error) {
+func runFilter(patches []*packages.ZypperPatch, exclusivePatches []string, 
excludes []*Exclude, pkgUpdates []*packages.PkgInfo, pkgToPatchesMap 
map[string][]string, withUpdate bool) ([]*packages.ZypperPatch, 
[]*packages.PkgInfo, error) {
        // exclusive patches
        var fPatches []*packages.ZypperPatch
        var fPkgs []*packages.PkgInfo
@@ -209,7 +209,8 @@
        // as per the configurations provided by user;
        // we remove the excluded patches from the list
        for _, patch := range patches {
-               if !containsString(excludes, patch.Name) {
+               // in zypper we're filtering patches instead of packages, but 
the method is still the same
+               if !shouldPackageBeExcluded(excludes, &patch.Name) {
                        fPatches = append(fPatches, patch)
                }
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/ospatch/zypper_patch_test.go 
new/osconfig-20220314.01/ospatch/zypper_patch_test.go
--- old/osconfig-20220209.00/ospatch/zypper_patch_test.go       2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/ospatch/zypper_patch_test.go       2022-03-14 
19:59:56.000000000 +0100
@@ -14,7 +14,7 @@
                pkgUpdates        []*packages.PkgInfo
                pkgToPatchesMap   map[string][]string
                exclusiveIncludes []string
-               excludes          []string
+               excludes          []*Exclude
                withUpdate        bool
        }
        type expect struct {
@@ -23,30 +23,32 @@
                err        error
        }
 
+       var patch3String = "patch-3"
+
        tests := []struct {
                name   string
                input  input
                expect expect
        }{
                {name: "runfilterwithexclusivepatches",
-                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{"patch-3"}, 
excludes: []string{}, withUpdate: false},
+                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{"patch-3"}, 
excludes: []*Exclude{}, withUpdate: false},
                        expect: expect{patches: []string{"patch-3"}, 
pkgUpdates: []string{}, err: nil},
                },
                {name: "runFilterwithUpdatewithexcludes",
                        // withupdate, exclude a patch that has
-                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]string{"patch-3"}, withUpdate: true},
+                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]*Exclude{CreateStringExclude(&patch3String)}, withUpdate: true},
                        expect: expect{patches: []string{"patch-1", "patch-2"}, 
pkgUpdates: []string{"pkg6"}, err: nil},
                },
                {name: "runFilterwithoutUpdatewithexcludes",
-                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]string{"patch-3"}, withUpdate: false},
+                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]*Exclude{CreateStringExclude(&patch3String)}, withUpdate: false},
                        expect: expect{patches: []string{"patch-1", "patch-2"}, 
pkgUpdates: []string{}, err: nil},
                },
                {name: "runFilterwithUpdatewithoutexcludes",
-                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]string{}, withUpdate: true},
+                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]*Exclude{}, withUpdate: true},
                        expect: expect{patches: []string{"patch-1", "patch-2", 
"patch-3"}, pkgUpdates: []string{"pkg6"}, err: nil},
                },
                {name: "runFilterwithoutUpdatewithoutexcludes",
-                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]string{}, withUpdate: false},
+                       input:  input{patches: patches, pkgUpdates: pkgUpdates, 
pkgToPatchesMap: pkgToPatchesMap, exclusiveIncludes: []string{}, excludes: 
[]*Exclude{}, withUpdate: false},
                        expect: expect{patches: []string{"patch-1", "patch-2", 
"patch-3"}, pkgUpdates: []string{}, err: nil},
                },
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/apt_deb.go 
new/osconfig-20220314.01/packages/apt_deb.go
--- old/osconfig-20220209.00/packages/apt_deb.go        2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/packages/apt_deb.go        2022-03-14 
19:59:56.000000000 +0100
@@ -116,6 +116,31 @@
        return true
 }
 
+type cmdModifier func(*exec.Cmd)
+
+func runAptGet(ctx context.Context, args []string, cmdModifiers []cmdModifier) 
([]byte, []byte, error) {
+       cmd := exec.CommandContext(ctx, aptGet, args...)
+       for _, modifier := range cmdModifiers {
+               modifier(cmd)
+       }
+
+       return runner.Run(ctx, cmd)
+}
+
+func runAptGetWithDowngradeRetrial(ctx context.Context, args []string, 
cmdModifiers []cmdModifier) ([]byte, []byte, error) {
+       stdout, stderr, err := runAptGet(ctx, args, cmdModifiers)
+       if err != nil {
+               if strings.Contains(string(stderr), "E: Packages were 
downgraded and -y was used without --allow-downgrades.") {
+                       cmdModifiers = append(cmdModifiers, func(cmd *exec.Cmd) 
{
+                               cmd.Args = append(cmd.Args, allowDowngradesArg)
+                       })
+                       stdout, stderr, err = runAptGet(ctx, args, cmdModifiers)
+               }
+       }
+
+       return stdout, stderr, err
+}
+
 func parseDpkgDeb(data []byte) (*PkgInfo, error) {
        /*
           new Debian package, version 2.0.
@@ -185,14 +210,15 @@
 // InstallAptPackages installs apt packages.
 func InstallAptPackages(ctx context.Context, pkgs []string) error {
        args := append(aptGetInstallArgs, pkgs...)
-       install := exec.CommandContext(ctx, aptGet, args...)
-       install.Env = append(os.Environ(),
-               "DEBIAN_FRONTEND=noninteractive",
-       )
-       stdout, stderr, err := runner.Run(ctx, install)
+       cmdModifiers := []cmdModifier{
+               func(cmd *exec.Cmd) {
+                       cmd.Env = append(os.Environ(), 
"DEBIAN_FRONTEND=noninteractive")
+               },
+       }
+       stdout, stderr, err := runAptGetWithDowngradeRetrial(ctx, args, 
cmdModifiers)
        if err != nil {
                if dpkgRepair(ctx, stderr) {
-                       stdout, stderr, err = runner.Run(ctx, install)
+                       stdout, stderr, err = 
runAptGetWithDowngradeRetrial(ctx, args, cmdModifiers)
                }
        }
        if err != nil {
@@ -204,14 +230,15 @@
 // RemoveAptPackages removes apt packages.
 func RemoveAptPackages(ctx context.Context, pkgs []string) error {
        args := append(aptGetRemoveArgs, pkgs...)
-       remove := exec.CommandContext(ctx, aptGet, args...)
-       remove.Env = append(os.Environ(),
-               "DEBIAN_FRONTEND=noninteractive",
-       )
-       stdout, stderr, err := runner.Run(ctx, remove)
+       cmdModifiers := []cmdModifier{
+               func(cmd *exec.Cmd) {
+                       cmd.Env = append(os.Environ(), 
"DEBIAN_FRONTEND=noninteractive")
+               },
+       }
+       stdout, stderr, err := runAptGet(ctx, args, cmdModifiers)
        if err != nil {
                if dpkgRepair(ctx, stderr) {
-                       stdout, stderr, err = runner.Run(ctx, remove)
+                       stdout, stderr, err = runAptGet(ctx, args, cmdModifiers)
                }
        }
        if err != nil {
@@ -292,7 +319,7 @@
                return nil, err
        }
 
-       out, err := run(ctx, aptGet, args)
+       out, _, err := runAptGetWithDowngradeRetrial(ctx, args, []cmdModifier{})
        if err != nil {
                return nil, err
        }
@@ -302,7 +329,8 @@
 
 // AptUpdate runs apt-get update.
 func AptUpdate(ctx context.Context) ([]byte, error) {
-       return run(ctx, aptGet, aptGetUpdateArgs)
+       stdout, _, err := runAptGet(ctx, aptGetUpdateArgs, []cmdModifier{})
+       return stdout, err
 }
 
 func parseInstalledDebpackages(data []byte) []*PkgInfo {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/apt_deb_test.go 
new/osconfig-20220314.01/packages/apt_deb_test.go
--- old/osconfig-20220209.00/packages/apt_deb_test.go   2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/packages/apt_deb_test.go   2022-03-14 
19:59:56.000000000 +0100
@@ -228,3 +228,23 @@
                t.Errorf("did not get expected error")
        }
 }
+
+func TestAllowDowngradesLogic(t *testing.T) {
+       mockCtrl := gomock.NewController(t)
+       defer mockCtrl.Finish()
+
+       mockCommandRunner := utilmocks.NewMockCommandRunner(mockCtrl)
+       runner = mockCommandRunner
+       cmdWithoutAllowDowngradesFlag := 
exec.CommandContext(context.Background(), aptGet, []string{}...)
+       cmdWithAllowDowngradesFlag := exec.CommandContext(context.Background(), 
aptGet, []string{"--allow-downgrades"}...)
+
+       mockCommandRunner.EXPECT().Run(testCtx, 
cmdWithoutAllowDowngradesFlag).Return([]byte(""), []byte("E: Packages were 
downgraded and -y was used without --allow-downgrades.\n"), 
errors.New("error")).Times(1)
+       stdoutBytes := []byte("stdout")
+       stderrBytes := []byte("stderr")
+       mockCommandRunner.EXPECT().Run(testCtx, 
cmdWithAllowDowngradesFlag).Return(stdoutBytes, stderrBytes, nil).Times(1)
+
+       stdout, stderr, err := runAptGetWithDowngradeRetrial(testCtx, 
[]string{}, []cmdModifier{})
+       if err != nil || !reflect.DeepEqual(stderr, stderrBytes) || 
!reflect.DeepEqual(stdout, stdoutBytes) {
+               t.Errorf("unexpected output: err - %v, stderr - %v, stdout - 
%v", err, string(stderr), string(stdout))
+       }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/cos.go 
new/osconfig-20220314.01/packages/cos.go
--- old/osconfig-20220209.00/packages/cos.go    2022-02-09 02:56:07.000000000 
+0100
+++ new/osconfig-20220314.01/packages/cos.go    2022-03-14 19:59:56.000000000 
+0100
@@ -14,9 +14,9 @@
 
 // Only build for linux but not on unsupported architectures.
 
-//go:build linux && (386 || amd64)
+//go:build linux && (386 || amd64 || arm64)
 // +build linux
-// +build 386 amd64
+// +build 386 amd64 arm64
 
 package packages
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/cos_stub.go 
new/osconfig-20220314.01/packages/cos_stub.go
--- old/osconfig-20220209.00/packages/cos_stub.go       2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/packages/cos_stub.go       2022-03-14 
19:59:56.000000000 +0100
@@ -14,8 +14,8 @@
 
 // Stub for linux builds.
 
-//go:build linux && !386 && !amd64
-// +build linux,!386,!amd64
+//go:build linux && !386 && !amd64 && !arm64
+// +build linux,!386,!amd64,!arm64
 
 package packages
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/yum.go 
new/osconfig-20220314.01/packages/yum.go
--- old/osconfig-20220209.00/packages/yum.go    2022-02-09 02:56:07.000000000 
+0100
+++ new/osconfig-20220314.01/packages/yum.go    2022-03-14 19:59:56.000000000 
+0100
@@ -43,7 +43,6 @@
 }
 
 type yumUpdateOpts struct {
-       excludes []string
        security bool
        minimal  bool
 }
@@ -67,14 +66,6 @@
        }
 }
 
-// YumExcludes returns a YumUpdateOption that specifies the excludes
-// command should be used.
-func YumExcludes(excludes []string) YumUpdateOption {
-       return func(args *yumUpdateOpts) {
-               args.excludes = excludes
-       }
-}
-
 // InstallYumPackages installs yum packages.
 func InstallYumPackages(ctx context.Context, pkgs []string) error {
        _, err := run(ctx, yum, append(yumInstallArgs, pkgs...))
@@ -167,7 +158,6 @@
        yumOpts := &yumUpdateOpts{
                security: false,
                minimal:  false,
-               excludes: []string{},
        }
 
        for _, opt := range opts {
@@ -181,11 +171,6 @@
        if yumOpts.security {
                args = append(args, "--security")
        }
-       if len(yumOpts.excludes) > 0 {
-               for _, pkg := range yumOpts.excludes {
-                       args = append(args, []string{"--exclude", pkg}...)
-               }
-       }
 
        stdout, stderr, err := ptyrunner.Run(ctx, exec.CommandContext(ctx, yum, 
args...))
        if err != nil {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/osconfig-20220209.00/packages/yum_test.go 
new/osconfig-20220314.01/packages/yum_test.go
--- old/osconfig-20220209.00/packages/yum_test.go       2022-02-09 
02:56:07.000000000 +0100
+++ new/osconfig-20220314.01/packages/yum_test.go       2022-03-14 
19:59:56.000000000 +0100
@@ -154,29 +154,28 @@
                }
        })
 
-       // Test WithSecurityWithExcludes
-       t.Run("WithSecurityWithExcludes", func(t *testing.T) {
-               // the mock data returned by mockcommandrunner will not include 
this
-               // package anyways. The purpose of this test is to make sure 
that
-               // when customer specifies excluded packages, we set the 
--exclude flag
-               // in the yum command.
-               excludedPackages := []string{"ex-pkg1", "ex-pkg2"}
-               expectedCmd := exec.CommandContext(context.Background(), yum, 
append(yumListUpdatesArgs, "--security", "--exclude", excludedPackages[0], 
"--exclude", excludedPackages[1])...)
+       /*      // Test WithSecurityWithExcludes
+               t.Run("WithSecurityWithExcludes", func(t *testing.T) {
+                       // the mock data returned by mockcommandrunner will not 
include this
+                       // package anyways. The purpose of this test is to make 
sure that
+                       // when customer specifies excluded packages, we set 
the --exclude flag
+                       // in the yum command.
+                       expectedCmd := 
exec.CommandContext(context.Background(), yum, append(yumListUpdatesArgs, 
"--security")...)
 
-               first := mockCommandRunner.EXPECT().Run(testCtx, 
expectedCheckUpdate).Return(data, []byte("stderr"), errExit100).Times(1)
-               mockCommandRunner.EXPECT().Run(testCtx, 
expectedCmd).After(first).Return(data, []byte("stderr"), nil).Times(1)
-               ret, err := YumUpdates(testCtx, YumUpdateMinimal(false), 
YumUpdateSecurity(true), YumExcludes(excludedPackages))
-               if err != nil {
-                       t.Errorf("did not expect error: %v", err)
-               }
+                       first := mockCommandRunner.EXPECT().Run(testCtx, 
expectedCheckUpdate).Return(data, []byte("stderr"), errExit100).Times(1)
+                       mockCommandRunner.EXPECT().Run(testCtx, 
expectedCmd).After(first).Return(data, []byte("stderr"), nil).Times(1)
+                       ret, err := YumUpdates(testCtx, 
YumUpdateMinimal(false), YumUpdateSecurity(true))
+                       if err != nil {
+                               t.Errorf("did not expect error: %v", err)
+                       }
 
-               allPackageNames := []string{"kernel", "foo", "bar"}
-               for _, pkg := range ret {
-                       if !contains(allPackageNames, pkg.Name) {
-                               t.Errorf("package %s expected to be present.", 
pkg.Name)
+                       allPackageNames := []string{"kernel", "foo", "bar"}
+                       for _, pkg := range ret {
+                               if !contains(allPackageNames, pkg.Name) {
+                                       t.Errorf("package %s expected to be 
present.", pkg.Name)
+                               }
                        }
-               }
-       })
+               })*/
 }
 
 func contains(names []string, name string) bool {

++++++ vendor.tar.gz ++++++

Reply via email to