@@ -6,6 +6,7 @@ package imds
66import (
77 "context"
88 "encoding/json"
9+ "net"
910 "net/http"
1011 "net/url"
1112
@@ -46,7 +47,10 @@ func RetryAttempts(attempts uint) ClientOption {
4647const (
4748 vmUniqueIDProperty = "vmId"
4849 imdsComputePath = "/metadata/instance/compute"
49- imdsComputeAPIVersion = "api-version=2021-01-01"
50+ imdsNetworkPath = "/metadata/instance/network"
51+ imdsVersionsPath = "/metadata/versions"
52+ imdsDefaultAPIVersion = "api-version=2021-01-01"
53+ imdsNCDetailsVersion = "api-version=2025-07-24"
5054 imdsFormatJSON = "format=json"
5155 metadataHeaderKey = "Metadata"
5256 metadataHeaderValue = "true"
@@ -79,7 +83,7 @@ func NewClient(opts ...ClientOption) *Client {
7983func (c * Client ) GetVMUniqueID (ctx context.Context ) (string , error ) {
8084 var vmUniqueID string
8185 err := retry .Do (func () error {
82- computeDoc , err := c .getInstanceComputeMetadata (ctx )
86+ computeDoc , err := c .getInstanceMetadata (ctx , imdsComputePath , imdsDefaultAPIVersion )
8387 if err != nil {
8488 return errors .Wrap (err , "error getting IMDS compute metadata" )
8589 }
@@ -102,14 +106,40 @@ func (c *Client) GetVMUniqueID(ctx context.Context) (string, error) {
102106 return vmUniqueID , nil
103107}
104108
105- func (c * Client ) getInstanceComputeMetadata (ctx context.Context ) (map [string ]any , error ) {
106- imdsComputeURL , err := url .JoinPath (c .config .endpoint , imdsComputePath )
109+ func (c * Client ) GetNetworkInterfaces (ctx context.Context ) ([]NetworkInterface , error ) {
110+ var networkData NetworkInterfaces
111+ err := retry .Do (func () error {
112+ networkInterfaces , err := c .getInstanceMetadata (ctx , imdsNetworkPath , imdsNCDetailsVersion )
113+ if err != nil {
114+ return errors .Wrap (err , "error getting IMDS network metadata" )
115+ }
116+
117+ // Parse the network metadata to the expected structure
118+ jsonData , err := json .Marshal (networkInterfaces )
119+ if err != nil {
120+ return errors .Wrap (err , "error marshaling network metadata" )
121+ }
122+
123+ if err := json .Unmarshal (jsonData , & networkData ); err != nil {
124+ return errors .Wrap (err , "error unmarshaling network metadata" )
125+ }
126+ return nil
127+ }, retry .Context (ctx ), retry .Attempts (c .config .retryAttempts ), retry .DelayType (retry .BackOffDelay ))
107128 if err != nil {
108- return nil , errors .Wrap (err , "unable to build path to IMDS compute metadata " )
129+ return nil , errors .Wrap (err , "external call failed " )
109130 }
110- imdsComputeURL = imdsComputeURL + "?" + imdsComputeAPIVersion + "&" + imdsFormatJSON
111131
112- req , err := http .NewRequestWithContext (ctx , http .MethodGet , imdsComputeURL , http .NoBody )
132+ return networkData .Interface , nil
133+ }
134+
135+ func (c * Client ) getInstanceMetadata (ctx context.Context , imdsMetadataPath , imdsAPIVersion string ) (map [string ]any , error ) {
136+ imdsRequestURL , err := url .JoinPath (c .config .endpoint , imdsMetadataPath )
137+ if err != nil {
138+ return nil , errors .Wrap (err , "unable to build path to IMDS metadata for path" + imdsMetadataPath )
139+ }
140+ imdsRequestURL = imdsRequestURL + "?" + imdsAPIVersion + "&" + imdsFormatJSON
141+
142+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , imdsRequestURL , http .NoBody )
113143 if err != nil {
114144 return nil , errors .Wrap (err , "error building IMDS http request" )
115145 }
@@ -133,3 +163,86 @@ func (c *Client) getInstanceComputeMetadata(ctx context.Context) (map[string]any
133163
134164 return m , nil
135165}
166+
167+ func (c * Client ) GetIMDSVersions (ctx context.Context ) (* APIVersionsResponse , error ) {
168+ var versionsResp APIVersionsResponse
169+ err := retry .Do (func () error {
170+ // Build the URL for the versions endpoint
171+ imdsRequestURL , err := url .JoinPath (c .config .endpoint , imdsVersionsPath )
172+ if err != nil {
173+ return errors .Wrap (err , "unable to build path to IMDS versions endpoint" )
174+ }
175+
176+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , imdsRequestURL , http .NoBody )
177+ if err != nil {
178+ return errors .Wrap (err , "error building IMDS versions http request" )
179+ }
180+
181+ req .Header .Add (metadataHeaderKey , metadataHeaderValue )
182+ resp , err := c .cli .Do (req )
183+ if err != nil {
184+ return errors .Wrap (err , "error querying IMDS versions API" )
185+ }
186+ defer resp .Body .Close ()
187+
188+ if resp .StatusCode != http .StatusOK {
189+ return errors .Wrapf (ErrUnexpectedStatusCode , "unexpected status code %d" , resp .StatusCode )
190+ }
191+
192+ if err := json .NewDecoder (resp .Body ).Decode (& versionsResp ); err != nil {
193+ return errors .Wrap (err , "error decoding IMDS versions response as json" )
194+ }
195+
196+ return nil
197+ }, retry .Context (ctx ), retry .Attempts (c .config .retryAttempts ), retry .DelayType (retry .BackOffDelay ))
198+ if err != nil {
199+ return nil , errors .Wrap (err , "exhausted retries querying IMDS versions" )
200+ }
201+
202+ return & versionsResp , nil
203+ }
204+
205+ // Required for marshaling/unmarshaling of mac address
206+ type HardwareAddr net.HardwareAddr
207+
208+ func (h * HardwareAddr ) MarshalJSON () ([]byte , error ) {
209+ data , err := json .Marshal (net .HardwareAddr (* h ).String ())
210+ if err != nil {
211+ return nil , errors .Wrap (err , "failed to marshal hardware address" )
212+ }
213+ return data , nil
214+ }
215+
216+ func (h * HardwareAddr ) UnmarshalJSON (data []byte ) error {
217+ var s string
218+ if err := json .Unmarshal (data , & s ); err != nil {
219+ return errors .Wrap (err , "failed to unmarshal JSON data" )
220+ }
221+ mac , err := net .ParseMAC (s )
222+ if err != nil {
223+ return errors .Wrap (err , "failed to parse MAC address" )
224+ }
225+ * h = HardwareAddr (mac )
226+ return nil
227+ }
228+
229+ func (h * HardwareAddr ) String () string {
230+ return net .HardwareAddr (* h ).String ()
231+ }
232+
233+ // NetworkInterface represents a network interface from IMDS
234+ type NetworkInterface struct {
235+ // IMDS returns compartment fields - these are mapped to NC ID and NC version
236+ MacAddress HardwareAddr `json:"macAddress"`
237+ InterfaceCompartmentID string `json:"interfaceCompartmentID,omitempty"`
238+ }
239+
240+ // NetworkInterfaces represents the network interfaces from IMDS
241+ type NetworkInterfaces struct {
242+ Interface []NetworkInterface `json:"interface"`
243+ }
244+
245+ // APIVersionsResponse represents versions form IMDS
246+ type APIVersionsResponse struct {
247+ APIVersions []string `json:"apiVersions"`
248+ }
0 commit comments