Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package google-guest-agent for 
openSUSE:Factory checked in at 2024-08-05 17:22:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/google-guest-agent (Old)
 and      /work/SRC/openSUSE:Factory/.google-guest-agent.new.7232 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "google-guest-agent"

Mon Aug  5 17:22:22 2024 rev:37 rq:1191634 version:20240802.00

Changes:
--------
--- /work/SRC/openSUSE:Factory/google-guest-agent/google-guest-agent.changes    
2024-07-08 19:09:33.061572432 +0200
+++ 
/work/SRC/openSUSE:Factory/.google-guest-agent.new.7232/google-guest-agent.changes
  2024-08-05 17:23:24.156355551 +0200
@@ -1,0 +2,63 @@
+Mon Aug  5 07:26:29 UTC 2024 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to version 20240802.00
+  * Fix where agent panics on nil event (#409)
+- from version 20240801.00
+  * Configure primary nic if only set in cfg file (#408)
+  * Update NIC management strategy (#402)
+  * Only release dhclient leases for an interface if the respective dhclient 
is still running (#407)
+  * Disable OS Login without pruning off any extra suffix. (#400)
+  * Skip root cert rotation if installed once (#405)
+  * Add ipv6 support to guest agent (#404)
+  * Update Accounts documentation (#403)
+  * Update google-startup-scripts.service to enable logging (#399)
+  * Network subsystem remove os rules (#396)
+  * oslogin: don't remove sshca watcher when oslogin is disabled (#398)
+  * Update dependencies to catch up on CVE fixes (#397)
+  * Network manager netplan implementation (#386)
+  * Update dependencies to catch up on CVE fixes (#391)
+  * Log current available routes on error (#388)
+  * Fix command monitor bugs (#389)
+  * Windows account: ignore "user already belogs to group" error (#387)
+  * Add more error logging in snapshot handling requests, use common retry 
util (#384)
+  * All non-200 status code from MDS should raise error (#383)
+  * Change metadata key to enable-oslogin-certificates (#382)
+  * Update dhclient pid/lease file directory to abide apparmor rules (#381)
+  * Add COS homedir-gid patch to upstream. (#365)
+  * Add require-oslogin-certificates logic to disable keys (#368)
+  * systemd-networkd: support debian 12's version (#372)
+  * Minor update typo in comment (#380)
+  * NetworkManager: only set secondary interfaces as up (#378)
+  * address manager: make sure we check for oldMetadata (#375)
+  * network: early setup network (#374)
+  * NetworkManager: fix ipv6 and ipv4 mode attribute (#373)
+  * Network Manager: make sure we clean up ifcfg files (#371)
+  * metadata script runner: fix script download (#370)
+  * oslogin: avoid adding extra empty line at the end of 
/etc/security/group.conf (#369)
+  * Dynamic vlan (#361)
+  * Check for nil response (#366)
+  * Create NetworkManager implementation (#362)
+  * Skip interface manager on Windows (#363)
+  * network: remove ignore setup (#360)
+  * Create wicked network service implementation and its respective unit (#356)
+  * Update metadata script runner, add tests (#357)
+  * Refactor guest-agent to use common retry util (#355)
+  * Flush logs before exiting #358 (#359)
+  * Create systemd-networkd unit tests. (#354)
+  * Update network manager unit tests (#351)
+  * Implement retry util (#350)
+  * Refactor utils package to not dump everything unrelated into one file 
(#352)
+  * Set version on metadata script runner (#353)
+  * Implement cleanup of deprecated configuration directives (#348)
+  * Ignore DHCP offered routes only for secondary nics (#347)
+  * Deprecate DHClient in favor of systemd-networkd (#342)
+  * Generate windows and linux licenses (#346)
+  * Remove quintonamore from OWNERS (#345)
+  * Delete integration tests (#343)
+- from version 20240716.00
+  * Update dep: golang.org/x/crypto to v0.17.0
+  * Update dep: google.golang.org/protobuf to 1.33.0
+  * Update dep: golang.org/x/net to 0.17.0
+  * Update dep: google.golang.org/grpc to v1.57.1
+
+-------------------------------------------------------------------

Old:
----
  guest-agent-20240701.00.tar.gz

New:
----
  guest-agent-20240802.00.tar.gz

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

Other differences:
------------------
++++++ google-guest-agent.spec ++++++
--- /var/tmp/diff_new_pack.QcUaNl/_old  2024-08-05 17:23:25.368405254 +0200
+++ /var/tmp/diff_new_pack.QcUaNl/_new  2024-08-05 17:23:25.368405254 +0200
@@ -24,7 +24,7 @@
 %global import_path     %{provider_prefix}
 
 Name:           google-guest-agent
-Version:        20240701.00
+Version:        20240802.00
 Release:        0
 Summary:        Google Cloud Guest Agent
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.QcUaNl/_old  2024-08-05 17:23:25.408406894 +0200
+++ /var/tmp/diff_new_pack.QcUaNl/_new  2024-08-05 17:23:25.412407058 +0200
@@ -3,8 +3,8 @@
     <param 
name="url">https://github.com/GoogleCloudPlatform/guest-agent/</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="versionformat">20240701.00</param>
-    <param name="revision">20240701.00</param>
+    <param name="versionformat">20240802.00</param>
+    <param name="revision">20240802.00</param>
     <param name="changesgenerate">enable</param>
   </service>
   <service name="recompress" mode="disabled">
@@ -15,7 +15,7 @@
     <param name="basename">guest-agent</param>
   </service>
   <service name="go_modules" mode="disabled">
-    <param name="archive">guest-agent-20240701.00.tar.gz</param>
+    <param name="archive">guest-agent-20240802.00.tar.gz</param>
   </service>
 </services>
 

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.QcUaNl/_old  2024-08-05 17:23:25.432407879 +0200
+++ /var/tmp/diff_new_pack.QcUaNl/_new  2024-08-05 17:23:25.436408043 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/GoogleCloudPlatform/guest-agent/</param>
-              <param 
name="changesrevision">79a1124a2cfb01675889e9a8a9ace93ae42475e4</param></service></servicedata>
+              <param 
name="changesrevision">d592f5648412dfefbfa12c3602d5cfd1892b054f</param></service></servicedata>
 (No newline at EOF)
 

++++++ guest-agent-20240701.00.tar.gz -> guest-agent-20240802.00.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/guest-agent-20240701.00/README.md 
new/guest-agent-20240802.00/README.md
--- old/guest-agent-20240701.00/README.md       2024-06-25 00:57:12.000000000 
+0200
+++ new/guest-agent-20240802.00/README.md       2024-08-02 21:57:07.000000000 
+0200
@@ -58,9 +58,12 @@
     `google-sudoers` group.
 *   The daemon stores a file in the guest to record which user accounts are
     managed by Google.
-*   User accounts not managed by Google are not touched by the accounts daemon.
+*   User accounts not managed by the agent are not touched by the accounts 
daemon.
 *   The authorized keys file for a Google managed user is deleted when all SSH
     keys for the user are removed from metadata.
+*   Users accounts managed by the agent will be added to the `groups` config
+    line in the `Accounts` section. If these groups do not exist, the agent
+    will not create them.
 
 #### OS Login
 
@@ -79,6 +82,9 @@
 If the user disables OS login via metadata, the configuration changes will be
 removed.
 
+Note that options under the `Accounts` section of the configuration do not 
apply
+to oslogin users.
+
 #### Clock Skew
 
 (Linux only)
@@ -183,7 +189,7 @@
 Section           | Option                 | Value
 ----------------- | ---------------------- | -----
 Accounts          | deprovision\_remove    | `true` makes deprovisioning a 
user destructive.
-Accounts          | groups                 | Comma separated list of groups 
for newly provisioned users.
+Accounts          | groups                 | Comma separated list of groups 
for newly provisioned users created from metadata ssh keys.
 Accounts          | useradd\_cmd           | Command string to create a new 
user.
 Accounts          | userdel\_cmd           | Command string to delete a user.
 Accounts          | usermod\_cmd           | Command string to modify a user's 
groups.
@@ -208,6 +214,7 @@
 MetadataScripts   | shutdown               | `false` disables shutdown script 
execution.
 NetworkInterfaces | setup                  | `false` skips network interface 
setup.
 NetworkInterfaces | ip\_forwarding         | `false` skips IP forwarding.
+NetworkInterfaces | manage\_primary\_nic   | `true` will start managing the 
primary NIC in addition to the secondary NICs.
 NetworkInterfaces | dhcp\_command          | String path for alternate dhcp 
executable used to enable network interfaces.
 OSLogin           | cert_authentication    | `false` prevents guest-agent from 
setting up sshd's `TrustedUserCAKeys`, `AuthorizedPrincipalsCommand` and 
`AuthorizedPrincipalsCommandUser` configuration keys. Default value: `true`.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/addresses.go 
new/guest-agent-20240802.00/google_guest_agent/addresses.go
--- old/guest-agent-20240701.00/google_guest_agent/addresses.go 2024-06-25 
00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/addresses.go 2024-08-02 
21:57:07.000000000 +0200
@@ -382,7 +382,14 @@
                        if runtime.GOOS == "windows" {
                                // Don't addAddress if this is already 
configured.
                                if !slices.Contains(configuredIPs, ip) {
-                                       err = addAddress(net.ParseIP(ip), 
net.IPv4Mask(255, 255, 255, 255), uint32(iface.Index))
+                                       // In case of forwardedIpv6 we get IPV6 
CIDR and Parse IP will return nil.
+                                       netip := net.ParseIP(ip)
+                                       if netip != nil && !isIPv6(netip) {
+                                               // Retains existing behavior 
for ipv4 addresses.
+                                               err = addAddress(netip, 
net.IPv4Mask(255, 255, 255, 255), uint32(iface.Index))
+                                       } else {
+                                               err = addIpv6Address(ip, 
uint32(iface.Index))
+                                       }
                                }
                        } else {
                                err = addLocalRoute(ctx, config, ip, iface.Name)
@@ -400,7 +407,14 @@
                                if !slices.Contains(configuredIPs, ip) {
                                        continue
                                }
-                               err = removeAddress(net.ParseIP(ip), 
uint32(iface.Index))
+                               netip := net.ParseIP(ip)
+                               // In case of forwardedIpv6 we get IPV6 CIDR 
and Parse IP will return nil.
+                               if netip != nil && !isIPv6(netip) {
+                                       // Retains existing behavior for ipv4 
addresses.
+                                       err = removeAddress(netip, 
net.IPv4Mask(255, 255, 255, 255), uint32(iface.Index))
+                               } else {
+                                       err = removeIpv6Address(ip, 
uint32(iface.Index))
+                               }
                        } else {
                                err = removeLocalRoute(ctx, config, ip, 
iface.Name)
                        }
@@ -417,6 +431,30 @@
                        }
                }
        }
+       logger.Infof("Completed adding/removing routes for aliases, forwarded 
IP and target-instance IPs")
 
        return nil
 }
+
+// isIPv6 returns true if the IP address is an IPv6 address.
+func isIPv6(ip net.IP) bool {
+       return ip.To4() == nil
+}
+
+// addIpv6Address adds given IP on the provided network interface.
+func addIpv6Address(s string, idx uint32) error {
+       ip, n, err := net.ParseCIDR(s)
+       if err != nil {
+               return err
+       }
+       return addAddress(ip, n.Mask, idx)
+}
+
+// removeIpv6Address removes the given IP on the network interface.
+func removeIpv6Address(s string, idx uint32) error {
+       ip, n, err := net.ParseCIDR(s)
+       if err != nil {
+               return err
+       }
+       return removeAddress(ip, n.Mask, idx)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/addresses_test.go 
new/guest-agent-20240802.00/google_guest_agent/addresses_test.go
--- old/guest-agent-20240701.00/google_guest_agent/addresses_test.go    
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/addresses_test.go    
2024-08-02 21:57:07.000000000 +0200
@@ -18,6 +18,7 @@
        "context"
        "encoding/json"
        "fmt"
+       "net"
        "reflect"
        "testing"
 
@@ -214,4 +215,31 @@
                        }
                })
        }
+}
+
+func TestIsIPv6(t *testing.T) {
+       tests := []struct {
+               ip   net.IP
+               want bool
+               name string
+       }{
+               {
+                       name: "valid_ipv4",
+                       ip:   net.ParseIP("1.2.3.4"),
+                       want: false,
+               },
+               {
+                       name: "valid_ipv6",
+                       ip:   net.ParseIP("fd20:b8f:5d95:2000:0:2::"),
+                       want: true,
+               },
+       }
+
+       for _, test := range tests {
+               t.Run(test.name, func(t *testing.T) {
+                       if got := isIPv6(test.ip); got != test.want {
+                               t.Errorf("isIPv6(%s) = %t, want %t", 
test.ip.String(), got, test.want)
+                       }
+               })
+       }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/addresses_unix.go 
new/guest-agent-20240802.00/google_guest_agent/addresses_unix.go
--- old/guest-agent-20240701.00/google_guest_agent/addresses_unix.go    
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/addresses_unix.go    
2024-08-02 21:57:07.000000000 +0200
@@ -23,7 +23,7 @@
 )
 
 // TODO: addLocalRoute and addRoute should be merged with the addition of 
ipForwardType to ipForwardEntry.
-func addIPForwardEntry(route ipForwardEntry) error {
+func addIPForwardEntry(ipForwardEntry) error {
        return errors.New("addIPForwardEntry unimplemented on non Windows 
systems")
 }
 
@@ -32,10 +32,10 @@
        return nil, errors.New("getIPForwardEntries unimplemented on non 
Windows systems")
 }
 
-func addAddress(ip net.IP, mask net.IPMask, index uint32) error {
+func addAddress(net.IP, net.IPMask, uint32) error {
        return errors.New("addAddress unimplemented on non Windows systems")
 }
 
-func removeAddress(ip net.IP, index uint32) error {
+func removeAddress(net.IP, net.IPMask, uint32) error {
        return errors.New("removeAddress unimplemented on non Windows systems")
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/addresses_windows.go 
new/guest-agent-20240802.00/google_guest_agent/addresses_windows.go
--- old/guest-agent-20240701.00/google_guest_agent/addresses_windows.go 
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/addresses_windows.go 
2024-08-02 21:57:07.000000000 +0200
@@ -25,6 +25,7 @@
        "syscall"
        "unsafe"
 
+       "github.com/GoogleCloudPlatform/guest-logging-go/logger"
        "golang.org/x/sys/windows"
 )
 
@@ -120,15 +121,32 @@
 )
 
 func addAddress(ip net.IP, mask net.IPMask, index uint32) error {
+       logger.Infof("Adding %q ip address", ip.String())
+       subnet, _ := mask.Size()
        // CreateUnicastIpAddressEntry only available Vista onwards.
-       if err := procCreateUnicastIpAddressEntry.Find(); err != nil {
+       // AddIPAddress api supports only ipv4 address.
+       if isIPv6(ip) {
+               return createUnicastIpAddressEntry6(ip, uint8(subnet), index)
+       }
+
+       // 
https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-addipaddress
+       if err := procCreateUnicastIpAddressEntry.Find(); err != nil && 
!isIPv6(ip) {
                return addIPAddress(ip, mask, index)
        }
-       subnet, _ := mask.Size()
        return createUnicastIpAddressEntry(ip, uint8(subnet), index)
 }
 
-func removeAddress(ip net.IP, index uint32) error {
+func removeAddress(ip net.IP, mask net.IPMask, index uint32) error {
+       logger.Infof("Removing %q ip address", ip.String())
+       subnet, _ := mask.Size()
+       if isIPv6(ip) {
+               // Unlike ipv4 that can be added either by addIPAddress or 
createUnicastIpAddressEntry
+               // ipv6 addresses can only be added by 
createUnicastIpAddressEntry6.
+               // Try removing them by deleteUnicastIpAddressEntry6 only as 
deleteIPAddress deletes
+               // IP address previously added using AddIPAddress only.
+               return deleteUnicastIpAddressEntry6(ip, uint8(subnet), index)
+       }
+
        // DeleteUnicastIpAddressEntry only available Vista onwards.
        if err := procDeleteUnicastIpAddressEntry.Find(); err != nil {
                return deleteIPAddress(ip)
@@ -136,6 +154,87 @@
        return deleteUnicastIpAddressEntry(ip, index)
 }
 
+// LUID represents the locally unique identifier (LUID) for a network 
interface.
+// 
https://learn.microsoft.com/en-us/windows/win32/api/ifdef/ns-ifdef-net_luid_lh
+type LUID uint64
+
+// RawSockaddrInet represents an IPv4/IPv6 address and family.
+// 
https://learn.microsoft.com/en-us/windows/win32/api/ws2ipdef/ns-ws2ipdef-sockaddr_inet
+type RawSockaddrInet struct {
+       Family uint16
+       data   [26]byte
+}
+
+// MIBUnicastIPAddressRow6 stores unicast IP address information used with for
+// ipforwarding addresses.
+// 
https://learn.microsoft.com/en-us/windows/win32/api/netioapi/ns-netioapi-mib_unicastipaddress_row
+type MIBUnicastIPAddressRow6 struct {
+       Address            RawSockaddrInet
+       InterfaceLuid      LUID
+       InterfaceIndex     uint32
+       PrefixOrigin       uint32
+       SuffixOrigin       uint32
+       ValidLifetime      uint32
+       PreferredLifetime  uint32
+       OnLinkPrefixLength uint8
+       SkipAsSource       bool
+}
+
+// setAddr is helper function to set net.IP in a structure windows syscalls 
understand.
+func (addr *RawSockaddrInet) setAddr(ip net.IP) {
+       addr6 := (*windows.RawSockaddrInet6)(unsafe.Pointer(addr))
+       addr6.Family = windows.AF_INET6
+       copy(addr6.Addr[:], ip)
+}
+
+// initIpRow6 initializes the MIB_UNICASTIPADDRESS_ROW6 struct based on Ip, 
prefix length
+// and the index.
+func initIpRow6(ip net.IP, prefix uint8, index uint32) 
(*MIBUnicastIPAddressRow6, error) {
+       ipRow := new(MIBUnicastIPAddressRow6)
+       // No return value.
+       procInitializeUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow)))
+
+       ipRow.InterfaceIndex = index
+       ipRow.OnLinkPrefixLength = prefix
+       // 
https://blogs.technet.microsoft.com/rmilne/2012/02/08/fine-grained-control-when-registering-multiple-ip-addresses-on-a-network-card/
+       ipRow.SkipAsSource = true
+
+       addr := RawSockaddrInet{}
+       addr.setAddr(ip)
+       ipRow.Address = addr
+       return ipRow, nil
+}
+
+// createUnicastIpAddressEntry6 adds a new unicast IPv6 address entry on local 
machine.
+func createUnicastIpAddressEntry6(ip net.IP, prefix uint8, index uint32) error 
{
+       logger.Infof("CreateUnicastIpAddressEntry6 %v, with prefix %d on 
interface %d", ip, prefix, index)
+
+       ipRow, err := initIpRow6(ip, prefix, index)
+       if err != nil {
+               return fmt.Errorf("initialize MIB_UNICASTIPADDRESS_ROW failed: 
%w", err)
+       }
+
+       if ret, _, err := 
procCreateUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 {
+               return fmt.Errorf("nonzero return code from 
CreateUnicastIpAddressEntry: %s, last err: %w", syscall.Errno(ret), err)
+       }
+       return nil
+}
+
+// deleteUnicastIpAddressEntry6 removes a unicast IPv6 address entry from 
local machine.
+func deleteUnicastIpAddressEntry6(ip net.IP, prefix uint8, index uint32) error 
{
+       logger.Infof("DeleteUnicastIpAddressEntry6 %v, with prefix %d on 
interface %d", ip, prefix, index)
+
+       ipRow, err := initIpRow6(ip, prefix, index)
+       if err != nil {
+               return fmt.Errorf("initialize MIB_UNICASTIPADDRESS_ROW failed: 
%w", err)
+       }
+
+       if ret, _, err := 
procDeleteUnicastIpAddressEntry.Call(uintptr(unsafe.Pointer(ipRow))); ret != 0 {
+               return fmt.Errorf("nonzero return code from 
DeleteUnicastIpAddressEntry: %s, last err: %w", syscall.Errno(ret), err)
+       }
+       return nil
+}
+
 func createUnicastIpAddressEntry(ip net.IP, prefix uint8, index uint32) error {
        ipRow := new(MIB_UNICASTIPADDRESS_ROW)
        // No return value.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/agentcrypto/mtls_mds.go 
new/guest-agent-20240802.00/google_guest_agent/agentcrypto/mtls_mds.go
--- old/guest-agent-20240701.00/google_guest_agent/agentcrypto/mtls_mds.go      
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/agentcrypto/mtls_mds.go      
2024-08-02 21:57:07.000000000 +0200
@@ -19,6 +19,7 @@
        "context"
        "fmt"
        "path/filepath"
+       "sync/atomic"
        "time"
 
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/uefi"
@@ -52,7 +53,15 @@
 
 // CredsJob implements job scheduler interface for generating/rotating 
credentials.
 type CredsJob struct {
+       // client is the client used for communicating with MDS.
        client metadata.MDSClientInterface
+       // rootCertsInstalled tracks if MDS root certificates were installed 
successfully
+       // atleast once. This allows to skip unnecessary work of refreshing 
root certs
+       // which are updated only when instance stops/starts which will restart 
agent
+       // as well. Allowing refresh on agent restarts regardless of instance 
reboots allows
+       // to fix any issues encountered with root certificate without having 
to restart
+       // compute instance.
+       rootCertsInstalled atomic.Bool
 }
 
 // New initializer new job.
@@ -160,16 +169,19 @@
 // $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer 
-like "*google.internal*" }
 // Invoke-RestMethod -Uri https://169.254.169.254 -Method Get -Headers 
@{"Metadata-Flavor"="Google"} -Certificate $cert
 func (j *CredsJob) Run(ctx context.Context) (bool, error) {
-       logger.Infof("Fetching Root CA cert...")
-
-       v, err := j.readRootCACert(googleRootCACertUEFIVar)
-       if err != nil {
-               return true, fmt.Errorf("failed to read Root CA cert with an 
error: %w", err)
-       }
-
-       if err := j.writeRootCACert(ctx, v.Content, 
filepath.Join(defaultCredsDir, rootCACertFileName)); err != nil {
-               return true, fmt.Errorf("failed to store Root CA cert with an 
error: %w", err)
+       if !j.rootCertsInstalled.Load() {
+               logger.Infof("Fetching Root CA cert...")
+               v, err := j.readRootCACert(googleRootCACertUEFIVar)
+               if err != nil {
+                       return true, fmt.Errorf("failed to read Root CA cert 
with an error: %w", err)
+               }
+
+               if err := j.writeRootCACert(ctx, v.Content, 
filepath.Join(defaultCredsDir, rootCACertFileName)); err != nil {
+                       return true, fmt.Errorf("failed to store Root CA cert 
with an error: %w", err)
+               }
        }
+       // Set only when agent has atleast one successful run for installing 
root certs.
+       j.rootCertsInstalled.Store(true)
 
        logger.Infof("Fetching client credentials...")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/cfg/cfg.go 
new/guest-agent-20240802.00/google_guest_agent/cfg/cfg.go
--- old/guest-agent-20240701.00/google_guest_agent/cfg/cfg.go   2024-06-25 
00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/cfg/cfg.go   2024-08-02 
21:57:07.000000000 +0200
@@ -87,6 +87,7 @@
 dhcp_command =
 ip_forwarding = true
 setup = true
+manage_primary_nic =
 
 [OSLogin]
 cert_authentication = true
@@ -260,9 +261,10 @@
 
 // NetworkInterfaces contains the configurations of NetworkInterfaces section.
 type NetworkInterfaces struct {
-       DHCPCommand  string `ini:"dhcp_command,omitempty"`
-       IPForwarding bool   `ini:"ip_forwarding,omitempty"`
-       Setup        bool   `ini:"setup,omitempty"`
+       DHCPCommand      string `ini:"dhcp_command,omitempty"`
+       IPForwarding     bool   `ini:"ip_forwarding,omitempty"`
+       Setup            bool   `ini:"setup,omitempty"`
+       ManagePrimaryNIC bool   `ini:"manage_primary_nic,omitempty"`
 }
 
 // Snapshots contains the configurations of Snapshots section.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/events/sshtrustedca/sshtrustedca_linux.go
 
new/guest-agent-20240802.00/google_guest_agent/events/sshtrustedca/sshtrustedca_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/events/sshtrustedca/sshtrustedca_linux.go
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/events/sshtrustedca/sshtrustedca_linux.go
    2024-08-02 21:57:07.000000000 +0200
@@ -20,6 +20,7 @@
        "os"
        "os/exec"
        "path/filepath"
+       "sync/atomic"
        "syscall"
        "time"
 
@@ -78,7 +79,7 @@
 
 // Run listens to ssh_trusted_ca's pipe open calls and report back the event.
 func (mp *Watcher) Run(ctx context.Context, evType string) (bool, interface{}, 
error) {
-       var canceled bool
+       var canceled atomic.Bool
 
        for mp.isWaitingWrite() {
                time.Sleep(10 * time.Millisecond)
@@ -95,7 +96,7 @@
                case <-cancelContext:
                        break
                case <-ctx.Done():
-                       canceled = true
+                       canceled.Store(true)
 
                        // Open the pipe as O_RDONLY to release the blocking 
open O_WRONLY.
                        pipeFile, err := os.OpenFile(mp.pipePath, os.O_RDONLY, 
0644)
@@ -127,7 +128,7 @@
 
        // Have we got a ctx.Done()? if so lets just return from here and 
unregister
        // the watcher.
-       if canceled {
+       if canceled.Load() {
                if err := pipeFile.Close(); err != nil {
                        logger.Errorf("Failed to close readonly pipe: %+v", err)
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/instance_setup.go 
new/guest-agent-20240802.00/google_guest_agent/instance_setup.go
--- old/guest-agent-20240701.00/google_guest_agent/instance_setup.go    
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/instance_setup.go    
2024-08-02 21:57:07.000000000 +0200
@@ -27,6 +27,7 @@
 
        
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/agentcrypto"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
+       network 
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/network/manager"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
        
"github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/scheduler"
        "github.com/GoogleCloudPlatform/guest-agent/retry"
@@ -157,7 +158,17 @@
                        newMetadata, err = mdsClient.Get(ctx)
                        if err != nil {
                                logger.Errorf("Failed to reach MDS(all retries 
exhausted): %+v", err)
-                               os.Exit(1)
+                               logger.Infof("Falling to OS default network 
configuration to attempt to recover.")
+                               if err := network.FallbackToDefault(ctx); err 
!= nil {
+                                       // Just log error and attempt to 
continue anyway, if we can't reach MDS
+                                       // we can't do anything.
+                                       logger.Errorf("Failed to rollback 
guest-agent network configuration: %v", err)
+                               }
+                               newMetadata, err = mdsClient.Get(ctx)
+                               if err != nil {
+                                       logger.Errorf("Failed to reach MDS 
after attempt to recover network configuration(all retries exhausted): %+v", 
err)
+                                       os.Exit(1)
+                               }
                        }
                }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/dhclient_linux.go
 
new/guest-agent-20240802.00/google_guest_agent/network/manager/dhclient_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/dhclient_linux.go
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/dhclient_linux.go
    2024-08-02 21:57:07.000000000 +0200
@@ -425,7 +425,12 @@
        var obtainIpv6Interfaces []string
        var releaseIpv6Interfaces []string
 
-       for _, iface := range interfaces {
+       for i, iface := range interfaces {
+               if !shouldManageInterface(i == 0) {
+                       // Do not setup anything for this interface to avoid 
duplicate processes.
+                       continue
+               }
+
                // Check for IPv4 interfaces for which to obtain a lease.
                processExists, err := dhclientProcessExists(ctx, iface, ipv4)
                if err != nil {
@@ -494,13 +499,25 @@
 
        // Release all the interface leases from dhclient.
        for _, iface := range googleInterfaces {
-               if err := runDhclient(ctx, ipv4, iface, true); err != nil {
-                       return err
+               ipv4Exists, err := dhclientProcessExists(ctx, iface, ipv4)
+               if err != nil {
+                       return fmt.Errorf("error checking if ipv4 dhclient 
process for %s exists: %v", iface, err)
+               }
+               if ipv4Exists {
+                       if err = runDhclient(ctx, ipv4, iface, true); err != 
nil {
+                               return err
+                       }
                }
        }
        for _, iface := range googleIpv6Interfaces {
-               if err := runDhclient(ctx, ipv6, iface, true); err != nil {
-                       return err
+               ipv6Exists, err := dhclientProcessExists(ctx, iface, ipv6)
+               if err != nil {
+                       return fmt.Errorf("error checking if ipv6 dhclient 
process for %s exists: %v", iface, err)
+               }
+               if ipv6Exists {
+                       if err = runDhclient(ctx, ipv6, iface, true); err != 
nil {
+                               return err
+                       }
                }
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/dhclient_linux_test.go
 
new/guest-agent-20240802.00/google_guest_agent/network/manager/dhclient_linux_test.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/dhclient_linux_test.go
       2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/dhclient_linux_test.go
       2024-08-02 21:57:07.000000000 +0200
@@ -25,6 +25,7 @@
        "testing"
        "time"
 
+       "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/osinfo"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/ps"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/run"
@@ -135,6 +136,9 @@
 // dhclientTestSetup sets up the test.
 func dhclientTestSetup(t *testing.T, opts dhclientTestOpts) {
        t.Helper()
+       if err := cfg.Load(nil); err != nil {
+               t.Fatalf("cfg.Load(nil) = %v, nil", err)
+       }
 
        // We have to mock dhclientProcessExists as we cannot mock where the ps
        // package checks for processes here.
@@ -276,42 +280,42 @@
        }{
                {
                        name:                "all-ipv4",
-                       testInterfaces:      []string{"obtain1", "obtain2"},
+                       testInterfaces:      []string{"primary1", "obtain2"},
                        testIpv6Interfaces:  []string{},
                        existFlags:          []bool{false, false},
                        ipVersions:          []ipVersion{ipv4, ipv4},
-                       expectedObtainIpv4:  []string{"obtain1", "obtain2"},
+                       expectedObtainIpv4:  []string{"obtain2"},
                        expectedObtainIpv6:  []string{},
                        expectedReleaseIpv6: []string{},
                },
                {
                        name:                "all-ipv6",
-                       testInterfaces:      []string{"obtain1", "obtain2"},
-                       testIpv6Interfaces:  []string{"obtain1", "obtain2"},
+                       testInterfaces:      []string{"primary1", "obtain2"},
+                       testIpv6Interfaces:  []string{"primary1", "obtain2"},
                        existFlags:          []bool{false, false},
                        ipVersions:          []ipVersion{ipv6, ipv6},
-                       expectedObtainIpv4:  []string{"obtain1", "obtain2"},
-                       expectedObtainIpv6:  []string{"obtain1", "obtain2"},
+                       expectedObtainIpv4:  []string{"obtain2"},
+                       expectedObtainIpv6:  []string{"obtain2"},
                        expectedReleaseIpv6: []string{},
                },
                {
                        name:                "ipv4-ipv6",
-                       testInterfaces:      []string{"obtain1", "obtain2"},
+                       testInterfaces:      []string{"primary1", "obtain2"},
                        testIpv6Interfaces:  []string{"obtain2"},
                        existFlags:          []bool{false, false},
                        ipVersions:          []ipVersion{ipv4, ipv6},
-                       expectedObtainIpv4:  []string{"obtain1", "obtain2"},
+                       expectedObtainIpv4:  []string{"obtain2"},
                        expectedObtainIpv6:  []string{"obtain2"},
                        expectedReleaseIpv6: []string{},
                },
                {
                        name:                "release-ipv6",
-                       testInterfaces:      []string{"obtain1", "release1"},
-                       testIpv6Interfaces:  []string{"obtain1"},
+                       testInterfaces:      []string{"primary1", "release1"},
+                       testIpv6Interfaces:  []string{"primary1"},
                        existFlags:          []bool{false, true},
                        ipVersions:          []ipVersion{ipv4, ipv6},
-                       expectedObtainIpv4:  []string{"obtain1", "release1"},
-                       expectedObtainIpv6:  []string{"obtain1"},
+                       expectedObtainIpv4:  []string{"release1"},
+                       expectedObtainIpv6:  []string{},
                        expectedReleaseIpv6: []string{"release1"},
                },
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/manager.go 
new/guest-agent-20240802.00/google_guest_agent/network/manager/manager.go
--- old/guest-agent-20240701.00/google_guest_agent/network/manager/manager.go   
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/network/manager/manager.go   
2024-08-02 21:57:07.000000000 +0200
@@ -19,6 +19,7 @@
 import (
        "context"
        "fmt"
+       "net"
 
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/cfg"
        "github.com/GoogleCloudPlatform/guest-agent/google_guest_agent/osinfo"
@@ -68,28 +69,6 @@
        VlanInterfaces map[int]metadata.VlanInterface
 }
 
-// osConfigRule describes matching rules for OS's, used for specifying either
-// network interfaces to ignore during setup or native config hookups.
-type osConfigRule struct {
-       // osNames is a list of OS's names matching the rule (as described by 
osInfo)
-       osNames []string
-
-       // majorVersions is a map of this OS's versions to ignore.
-       majorVersions map[int]bool
-
-       // action defines what action or exception to perform for this rule.
-       action osConfigAction
-}
-
-// osConfigAction defines the action to be taken in an osConfigRule.
-type osConfigAction struct {
-       // ignorePrimary determines whether to ignore the primary network 
interface.
-       ignorePrimary bool
-
-       // ignoreSecondary determines whether to ignore all non-primary network 
interfaces.
-       ignoreSecondary bool
-}
-
 // guestAgentSection is the section added to guest-agent-written ini files to 
indicate
 // that the ini file is managed by the agent.
 type guestAgentSection struct {
@@ -131,9 +110,9 @@
        return nil, fmt.Errorf("no network manager impl found for %s", iface)
 }
 
-// SetupInterfaces sets up all the network interfaces on the system, applying 
rules described
-// by osRules and using the native network manager service detected to be 
managing the primary
-// network interface.
+// SetupInterfaces sets up all secondary network interfaces on the system, and 
primary network
+// interface if enabled in the configuration using the native network manager 
service detected
+// to be managing the primary network interface.
 func SetupInterfaces(ctx context.Context, config *cfg.Sections, mds 
*metadata.Descriptor) error {
        // User may have disabled network interface setup entirely.
        if !config.NetworkInterfaces.Setup {
@@ -195,3 +174,56 @@
 
        return nil
 }
+
+// FallbackToDefault will attempt to rescue broken networking by rolling back
+// all guest-agent modifications to the network configuration.
+func FallbackToDefault(ctx context.Context) error {
+       nics, err := buildInterfacesFromAllPhysicalNICs()
+       if err != nil {
+               return fmt.Errorf("could not build list of NICs for fallback: 
%v", err)
+       }
+
+       // Rollback every NIC with every known network manager.
+       for _, svc := range knownNetworkManagers {
+               logger.Infof("Rolling back %s", svc.Name())
+               if err := svc.Rollback(ctx, nics); err != nil {
+                       logger.Errorf("Failed to roll back config for %s: %v", 
svc.Name(), err)
+               }
+       }
+
+       return nil
+}
+
+// Build a *Interfaces from all physical interfaces rather than the MDS.
+func buildInterfacesFromAllPhysicalNICs() (*Interfaces, error) {
+       nics := &Interfaces{
+               EthernetInterfaces: nil,
+               VlanInterfaces:     map[int]metadata.VlanInterface{},
+       }
+
+       interfaces, err := net.Interfaces()
+       if err != nil {
+               return nil, fmt.Errorf("failed to get interfaces: %v", err)
+       }
+
+       for _, iface := range interfaces {
+               mac := iface.HardwareAddr.String()
+               if mac == "" {
+                       continue
+               }
+               nics.EthernetInterfaces = append(nics.EthernetInterfaces, 
metadata.NetworkInterfaces{
+                       Mac: mac,
+               })
+       }
+
+       return nics, nil
+}
+
+// shouldManageInterface returns whether the guest agent should manage an 
interface
+// provided whether the interface of interest is the primary interface or not.
+func shouldManageInterface(isPrimary bool) bool {
+       if isPrimary {
+               return cfg.Get().NetworkInterfaces.ManagePrimaryNIC
+       }
+       return true
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/manager_linux.go 
new/guest-agent-20240802.00/google_guest_agent/network/manager/manager_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/manager_linux.go 
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/manager_linux.go 
    2024-08-02 21:57:07.000000000 +0200
@@ -18,7 +18,7 @@
        // knownNetworkManagers is a list of supported/available network 
managers.
        knownNetworkManagers = []Service{
                &netplan{
-                       netplanConfigDir:  "/etc/netplan/",
+                       netplanConfigDir:  "/run/netplan/",
                        networkdDropinDir: "/etc/systemd/network/",
                        priority:          20,
                },
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/manager_test.go 
new/guest-agent-20240802.00/google_guest_agent/network/manager/manager_test.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/manager_test.go  
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/manager_test.go  
    2024-08-02 21:57:07.000000000 +0200
@@ -40,10 +40,16 @@
 
        // managingError indicates whether isManaging() should return an error.
        managingError bool
+
+       // rollbackError indicates whether Rollback() should return an error.
+       rollbackError bool
+
+       // rolledBack indicates whether the network config was rolled back
+       rolledBack bool
 }
 
 // Name implements the Service interface.
-func (n mockService) Name() string {
+func (n *mockService) Name() string {
        if n.isFallback {
                return "fallback"
        }
@@ -52,11 +58,11 @@
 
 // Configure gives the opportunity for the Service implementation to adjust 
its configuration
 // based on the Guest Agent configuration.
-func (n mockService) Configure(ctx context.Context, config *cfg.Sections) {
+func (n *mockService) Configure(context.Context, *cfg.Sections) {
 }
 
 // IsManaging implements the Service interface.
-func (n mockService) IsManaging(ctx context.Context, iface string) (bool, 
error) {
+func (n *mockService) IsManaging(context.Context, string) (bool, error) {
        if n.managingError {
                return false, fmt.Errorf("mock error")
        }
@@ -64,17 +70,21 @@
 }
 
 // SetupEthernetInterface implements the Service interface.
-func (n mockService) SetupEthernetInterface(ctx context.Context, config 
*cfg.Sections, nics *Interfaces) error {
+func (n *mockService) SetupEthernetInterface(context.Context, *cfg.Sections, 
*Interfaces) error {
        return nil
 }
 
 // SetupVlanInterface implements the Service interface.
-func (n mockService) SetupVlanInterface(ctx context.Context, config 
*cfg.Sections, nics *Interfaces) error {
+func (n *mockService) SetupVlanInterface(context.Context, *cfg.Sections, 
*Interfaces) error {
        return nil
 }
 
 // Rollback implements the Service interface.
-func (n mockService) Rollback(ctx context.Context, nics *Interfaces) error {
+func (n *mockService) Rollback(context.Context, *Interfaces) error {
+       n.rolledBack = true
+       if n.rollbackError {
+               return fmt.Errorf("mock error")
+       }
        return nil
 }
 
@@ -109,7 +119,7 @@
                name string
 
                // managers are the list of mock services to register.
-               services []mockService
+               services []*mockService
 
                // expectedManager is the manager expected to be returned.
                expectedManager mockService
@@ -123,7 +133,7 @@
                // Base test case testing if it works.
                {
                        name: "no-error",
-                       services: []mockService{
+                       services: []*mockService{
                                {
                                        isFallback: false,
                                        isManaging: true,
@@ -142,7 +152,7 @@
                // Test if an error is returned if no network manager is found.
                {
                        name: "no-manager-found",
-                       services: []mockService{
+                       services: []*mockService{
                                {
                                        isFallback: false,
                                        isManaging: false,
@@ -158,7 +168,7 @@
                // Test if an error is returned if IsManaging() fails.
                {
                        name: "is-managing-fail",
-                       services: []mockService{
+                       services: []*mockService{
                                {
                                        isFallback:    false,
                                        managingError: true,
@@ -174,7 +184,7 @@
                // Test if the fallback service is returned if all other 
services are not detected.
                {
                        name: "fallback",
-                       services: []mockService{
+                       services: []*mockService{
                                {
                                        isFallback: false,
                                        isManaging: false,
@@ -231,62 +241,77 @@
                                t.Fatalf("no error returned when error 
expected, expected error: %s", test.expectedErrorMessage)
                        }
 
-                       if activeService.manager != test.expectedManager {
+                       if *activeService.manager.(*mockService) != 
test.expectedManager {
                                t.Fatalf("did not get expected network manager. 
Expected: %v, Actual: %v", test.expectedManager, activeService)
                        }
                })
        }
 }
 
-// TestFindOSRule tests whether findOSRule() correctly returns the expected 
values
-// depending on whether a matching rule exists or not.
-func TestFindOSRule(t *testing.T) {
+// TestRollbackToDefault ensures that all network managers are rolled back,
+// including the active manager.
+func TestFallbackToDefault(t *testing.T) {
        managerTestSetup()
+       ctx := context.Background()
 
-       tests := []struct {
-               // name is the name of the test.
-               name string
-
-               // rules are mock OSConfig rules.
-               rules []osConfigRule
-
-               // expectedNil indicates to expect a nil return when set to 
true.
-               expectedNil bool
-       }{
-               // ignoreRule exists.
-               {
-                       name: "ignore-exist",
-                       rules: []osConfigRule{
-                               {
-                                       osNames: []string{"test"},
-                                       majorVersions: map[int]bool{
-                                               testOSVersion: true,
-                                       },
-                                       action: osConfigAction{},
-                               },
-                       },
-                       expectedNil: false,
+       prevKnownNetworkManager := knownNetworkManagers
+       t.Cleanup(func() {
+               knownNetworkManagers = prevKnownNetworkManager
+       })
+       knownNetworkManagers = []Service{
+               &mockService{
+                       isManaging: true,
                },
-               // ignoreRule does not exist.
-               {
-                       name: "ignore-no-exist",
-                       rules: []osConfigRule{
-                               {
-                                       osNames: []string{"non-test"},
-                                       majorVersions: map[int]bool{
-                                               0: true,
-                                       },
-                                       action: osConfigAction{},
-                               },
-                       },
-                       expectedNil: true,
+               &mockService{
+                       isManaging:    true,
+                       rollbackError: true,
+               },
+               &mockService{
+                       isManaging: false,
                },
        }
 
-       // Run the tests.
-       for _, test := range tests {
-               t.Run(test.name, func(t *testing.T) {
-                       managerTestSetup()
-               })
+       if err := FallbackToDefault(ctx); err != nil {
+               t.Fatalf("FallbackToDefault(ctx) = %v, want nil", err)
+       }
+
+       for i, svc := range knownNetworkManagers {
+               if !svc.(*mockService).rolledBack {
+                       t.Errorf("knownNetworkManagers[%d].rolledBack = %t, 
want true", i, svc.(*mockService).rolledBack)
+               }
+       }
+}
+
+func TestBuildInterfacesFromAllPhysicalNICs(t *testing.T) {
+       nics, err := buildInterfacesFromAllPhysicalNICs()
+       if err != nil {
+               t.Fatalf("buildInterfacesFromAllPhysicalNICs() = %v, want nil", 
err)
+       }
+
+       for _, nic := range nics.EthernetInterfaces {
+               if _, err := GetInterfaceByMAC(nic.Mac); err != nil {
+                       t.Errorf("GetInterfaceByMAC(%q) = %v, want nil)", 
nic.Mac, err)
+               }
+       }
+}
+
+func TestShouldManageInterface(t *testing.T) {
+       if err := cfg.Load(nil); err != nil {
+               t.Fatalf("cfg.Load(nil) = %v, want nil", err)
+       }
+       if shouldManageInterface(true) {
+               t.Error("with default config, shouldManageInterface(isPrimary = 
true) = true, want false")
+       }
+       if !shouldManageInterface(false) {
+               t.Error("with default config, shouldManageInterface(isPrimary = 
false) = false, want true")
+       }
+       if err := 
cfg.Load([]byte("[NetworkInterfaces]\nmanage_primary_nic=true")); err != nil {
+               t.Fatalf("cfg.Load(%q) = %v, want nil", 
"[NetworkInterfaces]\nmanage_primary_nic=true", err)
+       }
+       if !shouldManageInterface(true) {
+               t.Error("with manage_primary_nic=false, 
shouldManageInterface(isPrimary = true) = false, want true")
+       }
+       if !shouldManageInterface(false) {
+               t.Error("with manage_primary_nic=false, 
shouldManageInterface(isPrimary = false) = false, want true")
        }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/netplan_linux.go 
new/guest-agent-20240802.00/google_guest_agent/network/manager/netplan_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/netplan_linux.go 
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/netplan_linux.go 
    2024-08-02 21:57:07.000000000 +0200
@@ -97,7 +97,7 @@
 type netplanDHCPOverrides struct {
        // When true, the domain name received from the DHCP server will be 
used as DNS
        // search domain over this link.
-       UseDomains bool `yaml:"use-domains,omitempty"`
+       UseDomains *bool `yaml:"use-domains,omitempty"`
 }
 
 // netplanMatch contains the keys uses to match an interface.
@@ -181,15 +181,6 @@
                return fmt.Errorf("error writing systemd-networkd's drop-in: 
%v", err)
        }
 
-       osInfo := osinfoGet()
-       // Debian 12 has a pretty generic matching netplan configuration for 
gce, until we have that
-       // changed we are only removing.
-       if osInfo.OS == "debian" && osInfo.Version.Major == 12 {
-               if err := os.Remove("/etc/netplan/90-default.yaml"); err != nil 
{
-                       logger.Debugf("Failed to remove default netplan config: 
%s", err)
-               }
-       }
-
        // Avoid restarting systemd-networkd.
        if err := run.Quiet(ctx, "networkctl", "reload"); err != nil {
                return fmt.Errorf("error reloading systemd-networkd network 
configs: %v", err)
@@ -216,6 +207,9 @@
        }
 
        for i, iface := range interfaces {
+               if !shouldManageInterface(i == 0) {
+                       continue
+               }
                logger.Debugf("writing systemd-networkd drop-in config for %s", 
iface)
 
                var dhcp = "ipv4"
@@ -283,6 +277,12 @@
        return nil
 }
 
+// shouldUseDomains returns true if interface index is 0.
+func shouldUseDomains(idx int) *bool {
+       res := idx == 0
+       return &res
+}
+
 // writeNetplanEthernetDropin selects the ethernet configuration, transforms it
 // into a netplan dropin format and writes it down to the netplan's drop-in 
directory.
 func (n netplan) writeNetplanEthernetDropin(mtuMap map[string]int, interfaces, 
ipv6Interfaces []string) error {
@@ -294,6 +294,9 @@
        }
 
        for i, iface := range interfaces {
+               if !shouldManageInterface(i == 0) {
+                       continue
+               }
                logger.Debugf("Adding %s(%d) to drop-in configuration.", iface, 
i)
 
                trueVal := true
@@ -301,7 +304,7 @@
                        Match:  netplanMatch{Name: iface},
                        DHCPv4: &trueVal,
                        DHCP4Overrides: &netplanDHCPOverrides{
-                               UseDomains: true,
+                               UseDomains: shouldUseDomains(i),
                        },
                }
 
@@ -312,7 +315,7 @@
                if slices.Contains(ipv6Interfaces, iface) {
                        ne.DHCPv6 = &trueVal
                        ne.DHCP6Overrides = &netplanDHCPOverrides{
-                               UseDomains: true,
+                               UseDomains: shouldUseDomains(i),
                        }
                }
 
@@ -329,6 +332,12 @@
 // write writes the netplan dropin file.
 func (n netplan) write(nd netplanDropin, suffix string) error {
        dropinFile := n.dropinFile(suffix)
+       dropinDir := filepath.Dir(dropinFile)
+       err := os.MkdirAll(dropinDir, 0755)
+       if err != nil {
+               return fmt.Errorf("failed to create networkd dropin dir: %w", 
err)
+       }
+
        if err := writeYamlFile(dropinFile, &nd); err != nil {
                return fmt.Errorf("error saving netplan drop-in file %s: %w", 
dropinFile, err)
        }
@@ -423,5 +432,12 @@
                }
        }
 
+       if err := run.Quiet(ctx, "networkctl", "reload"); err != nil {
+               return fmt.Errorf("error reloading systemd-networkd network 
configs: %v", err)
+       }
+       if err := run.Quiet(ctx, "netplan", "apply"); err != nil {
+               return fmt.Errorf("error applying netplan changes: %w", err)
+       }
+
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/network_manager_linux.go
 
new/guest-agent-20240802.00/google_guest_agent/network/manager/network_manager_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/network_manager_linux.go
     2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/network_manager_linux.go
     2024-08-02 21:57:07.000000000 +0200
@@ -256,5 +256,8 @@
                }
        }
 
+       if err := run.Quiet(ctx, "nmcli", "conn", "reload"); err != nil {
+               return fmt.Errorf("error reloading NetworkManager config cache: 
%v", err)
+       }
        return nil
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/network/manager/wicked_linux.go 
new/guest-agent-20240802.00/google_guest_agent/network/manager/wicked_linux.go
--- 
old/guest-agent-20240701.00/google_guest_agent/network/manager/wicked_linux.go  
    2024-06-25 00:57:12.000000000 +0200
+++ 
new/guest-agent-20240802.00/google_guest_agent/network/manager/wicked_linux.go  
    2024-08-02 21:57:07.000000000 +0200
@@ -55,7 +55,7 @@
 
 // Configure gives the opportunity for the Service implementation to adjust 
its configuration
 // based on the Guest Agent configuration.
-func (n wicked) Configure(ctx context.Context, config *cfg.Sections) {
+func (n *wicked) Configure(ctx context.Context, config *cfg.Sections) {
        wickedCommand, err := exec.LookPath("wicked")
        if err != nil {
                logger.Infof("failed to find wicked path, falling back to 
default: %+v", err)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/oslogin.go 
new/guest-agent-20240802.00/google_guest_agent/oslogin.go
--- old/guest-agent-20240701.00/google_guest_agent/oslogin.go   2024-06-25 
00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/oslogin.go   2024-08-02 
21:57:07.000000000 +0200
@@ -356,7 +356,7 @@
                        if enable && !present {
                                line += oslogin
                        } else if !enable && present {
-                               line = strings.TrimSuffix(line, oslogin)
+                               line = strings.Replace(line, oslogin, "", 1)
                        }
 
                        if runtime.GOOS == "freebsd" {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/oslogin_test.go 
new/guest-agent-20240802.00/google_guest_agent/oslogin_test.go
--- old/guest-agent-20240701.00/google_guest_agent/oslogin_test.go      
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/oslogin_test.go      
2024-08-02 21:57:07.000000000 +0200
@@ -173,6 +173,19 @@
                        },
                        enable: true,
                },
+               {
+                       contents: []string{
+                               "line1",
+                               "passwd: line2" + oslogin + " 
some_other_service",
+                               "group: line3" + oslogin + " another_service",
+                       },
+                       want: []string{
+                               "line1",
+                               "passwd: line2 some_other_service",
+                               "group: line3 another_service",
+                       },
+                       enable: false,
+               },
        }
 
        for idx, tt := range tests {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/guest-agent-20240701.00/google_guest_agent/sshca/sshca.go 
new/guest-agent-20240802.00/google_guest_agent/sshca/sshca.go
--- old/guest-agent-20240701.00/google_guest_agent/sshca/sshca.go       
2024-06-25 00:57:12.000000000 +0200
+++ new/guest-agent-20240802.00/google_guest_agent/sshca/sshca.go       
2024-08-02 21:57:07.000000000 +0200
@@ -63,7 +63,12 @@
        }
 
        // Make sure we close the pipe after we've done writing to it.
-       pipeData := evData.Data.(*sshtrustedca.PipeData)
+       pipeData, ok := evData.Data.(*sshtrustedca.PipeData)
+       if !ok {
+               logger.Errorf("Received invalid event data (%+v), ignoring this 
event and un-subscribing %s", evData.Data, evType)
+               return false
+       }
+
        defer func() {
                if err := pipeData.File.Close(); err != nil {
                        logger.Errorf("Failed to close pipe: %+v", err)

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

Reply via email to