Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions cmd/cloud-init-server/metadata_handlers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"fmt"
"net/http"
"strings"

Expand Down Expand Up @@ -51,29 +52,54 @@ func MetaDataHandler(smd smdclient.SMDClientInterface, store cistore.Store) http
var err error
// If this request includes an id, it can be interrpreted as an impersonation request
if urlId == "" {
log.Debug().Msg("no id specified in request, attempting to identify based on requesting IP")
ip := getActualRequestIP(r)
log.Debug().Msgf("requesting IP is: %s", ip)
// Get the component information from the SMD client
id, err = smd.IDfromIP(ip)
if err != nil {
log.Print(err)
http.Error(w, "Failed to retrieve node ID from IP address", http.StatusUnprocessableEntity)
log.Printf("did not find id from ip %s: %v", ip, err)
w.WriteHeader(http.StatusUnprocessableEntity)
return
} else {
log.Printf("xname %s with ip %s found\n", id, ip)
}
} else {
id = urlId
}
log.Debug().Msgf("Getting metadata for id: %s", id)
smdComponent, err := smd.ComponentInformation(id)
if err != nil {
log.Debug().Msgf("Failed to get component information for %s: %s", id, err)
// If the component information is not available, return a 404
http.Error(w, "Node not found in SMD. Instance-data not available", http.StatusNotFound)
if esr, ok := err.(smdclient.ErrSMDResponse); ok {
var msg string
var status int
switch esr.HTTPResponse.StatusCode {
case http.StatusNotFound:
msg = fmt.Sprintf("node %s not found in SMD", id)
status = http.StatusNotFound
default:
msg = fmt.Sprintf("failed to get component information for node %s: %v", id, err)
status = http.StatusInternalServerError
}
http.Error(w, msg, status)
} else {
log.Debug().Msgf("failed to get component information for node %s: %s", id, err)
http.Error(w, fmt.Sprintf("internal error occurred fetching component information for node %s", id), http.StatusInternalServerError)
}
return
}
groups, err := smd.GroupMembership(id)
if err != nil {
// If the group information is not available, return an empty list
groups = []string{}
if esr, ok := err.(smdclient.ErrSMDResponse); ok {
switch esr.HTTPResponse.StatusCode {
case http.StatusBadRequest:
http.Error(w, fmt.Sprintf("%s is not a valid xname for SMD", id), http.StatusBadRequest)
return
default:
// If the group information is not available, return an empty list
groups = []string{}
}
}
}
bootIP, err := smd.IPfromID(id)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/cloud-init-server/userdata_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func GroupUserDataHandler(smd smdclient.SMDClientInterface, store cistore.Store)
}

if !isUserInGroup(id, group, smd) {
http.Error(w, "Group not found", http.StatusNotFound)
http.Error(w, fmt.Sprintf("node %s is not a member of group %s (node and/or group may not exist in SMD)", id, group), http.StatusNotFound)
return
}

Expand Down
19 changes: 16 additions & 3 deletions cmd/cloud-init-server/vendordata_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ func VendorDataHandler(smd smdclient.SMDClientInterface, store cistore.Store) ht
var err error
// If this request includes an id, it can be interrpreted as an impersonation request
if urlId == "" {
log.Debug().Msg("no id specified in request, attempting to identify based on requesting IP")
ip := getActualRequestIP(r)
log.Debug().Msgf("requesting IP is: %s", ip)
// Get the component information from the SMD client
id, err = smd.IDfromIP(ip)
if err != nil {
log.Print(err)
log.Printf("did not find id from ip %s: %v", ip, err)
w.WriteHeader(http.StatusUnprocessableEntity)
return
} else {
Expand All @@ -45,7 +47,18 @@ func VendorDataHandler(smd smdclient.SMDClientInterface, store cistore.Store) ht
}
groups, err := smd.GroupMembership(id)
if err != nil {
log.Debug().Msgf("Error getting group membership: %s", err)
if esr, ok := err.(smdclient.ErrSMDResponse); ok {
switch esr.HTTPResponse.StatusCode {
case http.StatusNotFound:
log.Warn().Msgf("node %s not found in SMD, include list will be empty", id)
case http.StatusBadRequest:
log.Warn().Msgf("node %s is an invalid xname in SMD, include list will be empty", id)
default:
log.Error().Err(err).Msg("unhandled HTTP response from SMD, include list will be empty")
}
} else {
log.Error().Err(err).Msgf("failed to get group membership for id %s, include list will be empty", id)
}
}

clusterDefaults, err := store.GetClusterDefaults()
Expand All @@ -57,7 +70,7 @@ func VendorDataHandler(smd smdclient.SMDClientInterface, store cistore.Store) ht
}
extendedInstanceData, err := store.GetInstanceInfo(id)
if err != nil {
log.Err(err).Msg("Error getting instance info")
log.Err(err).Msgf("Error getting instance info for id %s", id)
}
if extendedInstanceData.CloudInitBaseURL != "" {
baseUrl = extendedInstanceData.CloudInitBaseURL
Expand Down
15 changes: 10 additions & 5 deletions internal/smdclient/SMDclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ type SMDClientInterface interface {
// golang lint
// Expand this client to handle more of the SMD API and work more directly with the resources it manages

var (
ErrUnmarshal = errors.New("cannot unmarshal JSON")
)

// SMDClient is a client for SMD
type SMDClient struct {
clusterName string
Expand Down Expand Up @@ -206,6 +202,10 @@ func (s *SMDClient) getSMD(ep string, smd interface{}) error {
defer func() {
_ = resp.Body.Close() // ignoring error on deferred Close
}()
if resp.StatusCode >= 400 {
log.Error().Msgf("SMD GET request went through, but returned unsuccessful HTTP response (HTTP %d)", resp.StatusCode)
return ErrSMDResponse{HTTPResponse: resp}
}
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Error().Err(err).Msg("failed to read response body")
Expand Down Expand Up @@ -339,7 +339,9 @@ func (s *SMDClient) MACfromID(id string) (string, error) {
// GroupMembership returns the group labels for the xname with the given ID
func (s *SMDClient) GroupMembership(id string) ([]string, error) {
if id == "" {
log.Err(errors.New("ID is empty")).Msg("ID is empty")
err := errors.New("ID is empty")
log.Err(err).Msg("failed to get group membership")
return []string{}, err
}
ml := new(sm.Membership)
ep := "/hsm/v2/memberships/" + id
Expand All @@ -352,6 +354,9 @@ func (s *SMDClient) GroupMembership(id string) ([]string, error) {

func (s *SMDClient) ComponentInformation(id string) (base.Component, error) {
var node base.Component
if strings.Trim(id, " \t") == "" {
return node, ErrEmptyID
}
ep := "/hsm/v2/State/Components/" + id
err := s.getSMD(ep, &node)
if err != nil {
Expand Down
21 changes: 21 additions & 0 deletions internal/smdclient/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package smdclient

import (
"errors"
"fmt"
"net/http"
)

var (
ErrUnmarshal = errors.New("cannot unmarshal JSON")
ErrEmptyID = errors.New("empty id")
)

// ErrSMDResponse contains the HTTP response of a REST API request to SMD.
type ErrSMDResponse struct {
HTTPResponse *http.Response
}

func (esr ErrSMDResponse) Error() string {
return fmt.Sprintf("SMD response returned %s", esr.HTTPResponse.Status)
}
Loading