@@ -22,6 +22,7 @@ package openstack
2222import (
2323 "context"
2424 "fmt"
25+ "net"
2526 "net/url"
2627 "os"
2728 "os/exec"
@@ -43,14 +44,6 @@ const (
4344 configDriveUserdataPath = "/openstack/latest/user_data"
4445)
4546
46- var (
47- metadataServiceUrl = url.URL {
48- Scheme : "http" ,
49- Host : "169.254.169.254" ,
50- Path : "openstack/latest/user_data" ,
51- }
52- )
53-
5447func init () {
5548 platform .Register (platform.Provider {
5649 Name : "openstack" ,
@@ -166,14 +159,81 @@ func fetchConfigFromDevice(logger *log.Logger, ctx context.Context, path string)
166159 return os .ReadFile (filepath .Join (mnt , configDriveUserdataPath ))
167160}
168161
162+ func FindIPv6InterfaceName () (string , error ) {
163+ interfaces , err := net .Interfaces ()
164+ if err != nil {
165+ return "" , err
166+ }
167+
168+ for _ , iface := range interfaces {
169+ // Check if the interface is up and not a loopback
170+ if iface .Flags & net .FlagUp != 0 && iface .Flags & net .FlagLoopback == 0 {
171+ addrs , err := iface .Addrs ()
172+ if err != nil {
173+ continue
174+ }
175+
176+ for _ , addr := range addrs {
177+ // Check if the address is an IPv6 global unicast address
178+ if ipnet , ok := addr .(* net.IPNet ); ok && ipnet .IP .To4 () == nil && ! ipnet .IP .IsLinkLocalUnicast () {
179+ return iface .Name , nil
180+ }
181+ }
182+ }
183+ }
184+
185+ return "" , fmt .Errorf ("no IPv6 interface name found" )
186+ }
187+
169188func fetchConfigFromMetadataService (f * resource.Fetcher ) ([]byte , error ) {
170- res , err := f .FetchToBuffer (metadataServiceUrl , resource.FetchOptions {})
189+ iface , err := FindIPv6InterfaceName ()
190+ if err != nil {
191+ return nil , err
192+ }
193+ var (
194+ ipv4MetadataServiceUrl = url.URL {
195+ Scheme : "http" ,
196+ Host : "169.254.169.254" ,
197+ Path : "openstack/latest/user_data" ,
198+ }
199+ ipv6MetadataServiceUrl = url.URL {
200+ Scheme : "http" ,
201+ Host : fmt .Sprintf ("[fe80::a9fe:a9fe%%%s]" , url .PathEscape (iface )),
202+ Path : "openstack/latest/user_data" ,
203+ }
204+ )
205+ var resIPv4 , resIPv6 []byte
206+ var errIPv4 , errIPv6 error
207+
208+ // Try IPv4 endpoint
209+ resIPv4 , errIPv4 = f .FetchToBuffer (ipv4MetadataServiceUrl , resource.FetchOptions {})
210+ if errIPv4 != nil && errIPv4 != resource .ErrNotFound {
211+ f .Logger .Err ("Failed to fetch config from IPv4: %v" , errIPv4 )
212+ }
171213
172- // the metadata server exists but doesn't contain any actual metadata,
173- // assume that there is no config specified
174- if err == resource .ErrNotFound {
175- return nil , nil
214+ // Try IPv6 endpoint
215+ resIPv6 , errIPv6 = f .FetchToBuffer (ipv6MetadataServiceUrl , resource.FetchOptions {})
216+ if errIPv6 != nil && errIPv6 != resource .ErrNotFound {
217+ f .Logger .Err ("Failed to fetch config from IPv6: %v" , errIPv6 )
218+ }
219+
220+ // If both IPv4 and IPv6 have valid data, combine them
221+ if resIPv4 != nil && resIPv6 != nil {
222+ return append (resIPv4 , resIPv6 ... ), nil
223+ } else if resIPv4 != nil {
224+ return resIPv4 , nil
225+ } else if resIPv6 != nil {
226+ return resIPv6 , nil
227+ }
228+
229+ // If both endpoints fail, return the appropriate error
230+ if errIPv4 != nil {
231+ return nil , errIPv4
232+ }
233+ if errIPv6 != nil {
234+ return nil , errIPv6
176235 }
177236
178- return res , err
237+ // If both endpoints return ErrNotFound
238+ return nil , nil
179239}
0 commit comments