internal/configprovider/userdata.go (97 lines of code) (raw):

package configprovider import ( "bytes" "fmt" "io" "mime" "mime/multipart" "net/mail" "strings" "github.com/aws/eks-hybrid/api" internalapi "github.com/aws/eks-hybrid/internal/api" apibridge "github.com/aws/eks-hybrid/internal/api/bridge" imds "github.com/aws/eks-hybrid/internal/aws/imds" ) const ( contentTypeHeader = "Content-Type" mimeBoundaryParam = "boundary" multipartContentTypePrefix = "multipart/" nodeConfigMediaType = "application/" + api.GroupName ) type userDataConfigProvider struct{} func NewUserDataConfigProvider() ConfigProvider { return &userDataConfigProvider{} } func (ics *userDataConfigProvider) Provide() (*internalapi.NodeConfig, error) { userData, err := imds.GetUserData() if err != nil { return nil, err } // if the MIME data fails to parse as a multipart document, then fall back // to parsing the entire userdata as the node config. if multipartReader, err := getMIMEMultipartReader(userData); err == nil { config, err := parseMultipart(multipartReader) if err != nil { return nil, err } return config, nil } else { config, err := apibridge.DecodeNodeConfig(userData) if err != nil { return nil, err } return config, nil } } func getMIMEMultipartReader(data []byte) (*multipart.Reader, error) { msg, err := mail.ReadMessage(bytes.NewReader(data)) if err != nil { return nil, err } mediaType, params, err := mime.ParseMediaType(msg.Header.Get(contentTypeHeader)) if err != nil { return nil, err } if !strings.HasPrefix(mediaType, multipartContentTypePrefix) { return nil, fmt.Errorf("MIME type is not multipart") } return multipart.NewReader(msg.Body, params[mimeBoundaryParam]), nil } func parseMultipart(userDataReader *multipart.Reader) (*internalapi.NodeConfig, error) { var nodeConfigs []*internalapi.NodeConfig for { part, err := userDataReader.NextPart() if err == io.EOF { break } if err != nil { return nil, err } if partHeader := part.Header.Get(contentTypeHeader); len(partHeader) > 0 { mediaType, _, err := mime.ParseMediaType(partHeader) if err != nil { return nil, err } if mediaType == nodeConfigMediaType { nodeConfigPart, err := io.ReadAll(part) if err != nil { return nil, err } decodedConfig, err := apibridge.DecodeNodeConfig(nodeConfigPart) if err != nil { return nil, err } nodeConfigs = append(nodeConfigs, decodedConfig) } } } if len(nodeConfigs) > 0 { config := nodeConfigs[0] for _, nodeConfig := range nodeConfigs[1:] { if err := config.Merge(nodeConfig); err != nil { return nil, err } } return config, nil } else { return nil, fmt.Errorf("Could not find NodeConfig within UserData") } }