Skip to content

Commit

Permalink
Decompress GZIP'd user data (#1762)
Browse files Browse the repository at this point in the history
  • Loading branch information
cartermckinnon committed May 23, 2024
1 parent 25b6f34 commit d87c6c4
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 105 deletions.
75 changes: 70 additions & 5 deletions nodeadm/internal/configprovider/userdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package configprovider

import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"io"
"mime"
Expand All @@ -22,17 +24,39 @@ const (
nodeConfigMediaType = "application/" + api.GroupName
)

type userDataConfigProvider struct{}
type userDataProvider interface {
GetUserData() ([]byte, error)
}

type imdsUserDataProvider struct{}

func (p *imdsUserDataProvider) GetUserData() ([]byte, error) {
return imds.GetUserData()
}

type userDataConfigProvider struct {
userDataProvider userDataProvider
}

func NewUserDataConfigProvider() ConfigProvider {
return &userDataConfigProvider{}
return &userDataConfigProvider{
userDataProvider: &imdsUserDataProvider{},
}
}

func (ics *userDataConfigProvider) Provide() (*internalapi.NodeConfig, error) {
userData, err := imds.GetUserData()
func (p *userDataConfigProvider) Provide() (*internalapi.NodeConfig, error) {
userData, err := p.userDataProvider.GetUserData()
if err != nil {
return nil, err
}
userData, err = decodeIfBase64(userData)
if err != nil {
return nil, fmt.Errorf("failed to decode user data: %v", err)
}
userData, err = decompressIfGZIP(userData)
if err != nil {
return nil, fmt.Errorf("failed to decompress user data: %v", 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 {
Expand Down Expand Up @@ -85,6 +109,14 @@ func parseMultipart(userDataReader *multipart.Reader) (*internalapi.NodeConfig,
if err != nil {
return nil, err
}
nodeConfigPart, err = decodeIfBase64(nodeConfigPart)
if err != nil {
return nil, err
}
nodeConfigPart, err = decompressIfGZIP(nodeConfigPart)
if err != nil {
return nil, err
}
decodedConfig, err := apibridge.DecodeNodeConfig(nodeConfigPart)
if err != nil {
return nil, err
Expand All @@ -102,6 +134,39 @@ func parseMultipart(userDataReader *multipart.Reader) (*internalapi.NodeConfig,
}
return config, nil
} else {
return nil, fmt.Errorf("Could not find NodeConfig within UserData")
return nil, fmt.Errorf("could not find NodeConfig within UserData")
}
}

func decodeIfBase64(data []byte) ([]byte, error) {
e := base64.StdEncoding
maxDecodedLen := e.DecodedLen(len(data))
decodedData := make([]byte, maxDecodedLen)
decodedLen, err := e.Decode(decodedData, data)
if err != nil {
return data, nil
}
return decodedData[:decodedLen], nil
}

// https://en.wikipedia.org/wiki/Gzip
const gzipMagicNumber = uint16(0x1f8b)

func decompressIfGZIP(data []byte) ([]byte, error) {
if len(data) < 2 {
return data, nil
}
preamble := uint16(data[0])<<8 | uint16(data[1])
if preamble == gzipMagicNumber {
reader, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("failed to create GZIP reader: %v", err)
}
if decompressed, err := io.ReadAll(reader); err != nil {
return nil, fmt.Errorf("failed to read from GZIP reader: %v", err)
} else {
return decompressed, nil
}
}
return data, nil
}
Loading

0 comments on commit d87c6c4

Please sign in to comment.