Skip to content
Open
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
18 changes: 9 additions & 9 deletions rest-api/api/pkg/api/model/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@ func (icr *APIInstanceCreateRequest) ValidateAndSetOperatingSystemData(cfg *conf
if len(userDataMap.Content) > 0 {
documentRoot = userDataMap.Content[0]

if documentRoot.Kind == yaml.MappingNode {
if util.PhoneHomeSupportsUserDataRoot(documentRoot) {
isUserDataValidYAML = true
}
}
Expand All @@ -815,7 +815,7 @@ func (icr *APIInstanceCreateRequest) ValidateAndSetOperatingSystemData(cfg *conf
// so we want to do this check silently and not alert people who
// are using non-YAML user-data.

if err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
if _, err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return validation.Errors{
"userData": errors.New("failed to disable phone-home in userData after processing phone home config"),
}
Expand All @@ -824,7 +824,7 @@ func (icr *APIInstanceCreateRequest) ValidateAndSetOperatingSystemData(cfg *conf
}

// If there's still user-data, marshal so that it can be stored in the DB later
if isUserDataValidYAML && len(documentRoot.Content) > 0 {
if isUserDataValidYAML && (documentRoot.Kind == yaml.SequenceNode || len(documentRoot.Content) > 0) {

byteUserData, err := yaml.Marshal(userDataMap)
if err != nil {
Expand Down Expand Up @@ -1114,7 +1114,7 @@ func (bicr *APIBatchInstanceCreateRequest) ValidateAndSetOperatingSystemData(cfg
if len(userDataMap.Content) > 0 {
documentRoot = userDataMap.Content[0]

if documentRoot.Kind == yaml.MappingNode {
if util.PhoneHomeSupportsUserDataRoot(documentRoot) {
isUserDataValidYAML = true
}
}
Expand All @@ -1135,15 +1135,15 @@ func (bicr *APIBatchInstanceCreateRequest) ValidateAndSetOperatingSystemData(cfg
}

} else if isUserDataValidYAML {
if err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
if _, err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
return validation.Errors{
"userData": errors.New("failed to disable phone-home in userData after processing phone home config"),
}
}
}

// If there's still user-data, marshal so that it can be stored in the DB later
if isUserDataValidYAML && len(documentRoot.Content) > 0 {
if isUserDataValidYAML && (documentRoot.Kind == yaml.SequenceNode || len(documentRoot.Content) > 0) {

byteUserData, err := yaml.Marshal(userDataMap)
if err != nil {
Expand Down Expand Up @@ -1401,7 +1401,7 @@ func (iur *APIInstanceUpdateRequest) ValidateAndSetOperatingSystemData(cfg *conf
if len(userDataMap.Content) > 0 {
documentRoot = userDataMap.Content[0]

if documentRoot.Kind == yaml.MappingNode {
if util.PhoneHomeSupportsUserDataRoot(documentRoot) {
isUserDataValidYAML = true
}
}
Expand All @@ -1427,15 +1427,15 @@ func (iur *APIInstanceUpdateRequest) ValidateAndSetOperatingSystemData(cfg *conf
// so we want to do this check silently and not alert people who
// are using non-YAML user-data.

if err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
if _, err := util.RemovePhoneHomeFromUserData(documentRoot, cutil.GetPtr(cfg.GetSitePhoneHomeUrl())); err != nil {
return validation.Errors{
"userData": errors.New("failed to disable phone-home in userData after processing phone home config"),
}
}
}

// If there's still user-data, marshal so that it can be stored in the DB later
if isUserDataValidYAML && len(documentRoot.Content) > 0 {
if isUserDataValidYAML && (documentRoot.Kind == yaml.SequenceNode || len(documentRoot.Content) > 0) {

byteUserData, err := yaml.Marshal(userDataMap)
if err != nil {
Expand Down
11 changes: 6 additions & 5 deletions rest-api/api/pkg/api/model/operatingsystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ func (oscr *APIOperatingSystemCreateRequest) ValidateAndSetUserData(phonehomeUrl
// counts as valid YAML.
if len(userDataMap.Content) > 0 {
documentRoot = userDataMap.Content[0]
if documentRoot.Kind == yaml.MappingNode {
if util.PhoneHomeSupportsUserDataRoot(documentRoot) {
isUserDataValidYAML = true
}
}
Expand Down Expand Up @@ -797,7 +797,7 @@ func (osur *APIOperatingSystemUpdateRequest) ValidateAndSetUserData(phonehomeUrl
// counts as valid YAML.
if len(userDataMap.Content) > 0 {
documentRoot = userDataMap.Content[0]
if documentRoot.Kind == yaml.MappingNode {
if util.PhoneHomeSupportsUserDataRoot(documentRoot) {
isUserDataValidYAML = true
}
}
Expand All @@ -824,7 +824,7 @@ func (osur *APIOperatingSystemUpdateRequest) ValidateAndSetUserData(phonehomeUrl
// but the UI will always send false if phone-home is unchecked,
// so we want to do this check silently and not alert people who
// are using non-YAML user-data.
if err := util.RemovePhoneHomeFromUserData(documentRoot, &phonehomeUrl); err != nil {
if _, err := util.RemovePhoneHomeFromUserData(documentRoot, &phonehomeUrl); err != nil {
return validation.Errors{
"userData": errors.New("failed to remove phone home config from userData"),
}
Expand All @@ -836,11 +836,12 @@ func (osur *APIOperatingSystemUpdateRequest) ValidateAndSetUserData(phonehomeUrl
return nil
}

if len(documentRoot.Content) == 0 {
if documentRoot.Kind == yaml.MappingNode && len(documentRoot.Content) == 0 {
// If we've arrived here, then the original user-data
// was valid, but phone-home has been disabled, and the
// phone-home block was the only thing in the original YAML,
// so just blank the DB field.
// so just blank the DB field. An emptied #cloud-config-archive is
// serialized below instead, so it keeps its header.
osur.UserData = cutil.GetPtr("")
return nil
}
Expand Down
78 changes: 78 additions & 0 deletions rest-api/api/pkg/api/model/operatingsystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,84 @@ func TestAPIOperatingSystemCreateRequest_ValidateAndSetUserData(t *testing.T) {
}
}

func TestAPIOperatingSystemCreateRequest_ValidateAndSetUserData_Archive(t *testing.T) {
const phoneHomeURL = "http://localhost/phone-home"

const archive = `#cloud-config-archive
- type: text/cloud-config
content: |
#cloud-config
packages:
- curl
`

t.Run("appends phone-home as a new entry in a cloud-config-archive", func(t *testing.T) {
req := APIOperatingSystemCreateRequest{
Name: "test-name",
TenantID: cutil.GetPtr(uuid.NewString()),
UserData: cutil.GetPtr(archive),
PhoneHomeEnabled: cutil.GetPtr(true),
}

require.NoError(t, req.ValidateAndSetUserData(phoneHomeURL))
require.NotNil(t, req.UserData)
assert.True(t, strings.HasPrefix(*req.UserData, "#cloud-config-archive\n"),
"archive header must be preserved: %s", *req.UserData)
assert.Contains(t, *req.UserData, phoneHomeURL)
})

t.Run("replaces a standalone phone-home entry rather than duplicating it", func(t *testing.T) {
withPhoneHome := archive + `- type: text/cloud-config
content: |
#cloud-config
phone_home:
url: http://existing
`
req := APIOperatingSystemCreateRequest{
Name: "test-name",
TenantID: cutil.GetPtr(uuid.NewString()),
UserData: cutil.GetPtr(withPhoneHome),
PhoneHomeEnabled: cutil.GetPtr(true),
}

require.NoError(t, req.ValidateAndSetUserData(phoneHomeURL))
require.NotNil(t, req.UserData)
assert.Contains(t, *req.UserData, phoneHomeURL)
assert.NotContains(t, *req.UserData, "http://existing")
assert.Equal(t, 1, strings.Count(*req.UserData, "phone_home:"))
})
}

func TestAPIOperatingSystemUpdateRequest_ValidateAndSetUserData_EmptiedArchiveKeepsHeader(t *testing.T) {
const phoneHomeURL = "http://localhost/phone-home"

// Disabling phone-home on an archive whose only entry was phone-home must
// leave a valid (empty) #cloud-config-archive, not blank the field.
existing := &cdbm.OperatingSystem{
ID: uuid.New(),
Name: "ab",
UserData: cutil.GetPtr(`#cloud-config-archive
- type: text/cloud-config
content: |
#cloud-config
phone_home:
url: ` + phoneHomeURL + `
`),
PhoneHomeEnabled: true,
Status: cdbm.OperatingSystemStatusReady,
Type: cdbm.OperatingSystemTypeIPXE,
CreatedBy: uuid.New(),
}

req := APIOperatingSystemUpdateRequest{PhoneHomeEnabled: cutil.GetPtr(false)}

require.NoError(t, req.ValidateAndSetUserData(phoneHomeURL, existing))
require.NotNil(t, req.UserData)
assert.True(t, strings.HasPrefix(*req.UserData, "#cloud-config-archive"),
"emptied archive must keep its header, got: %q", *req.UserData)
assert.NotContains(t, *req.UserData, "phone_home")
}

func TestAPIOperatingSystemUpdateRequest_ValidateAndSetUserData(t *testing.T) {
type fields struct {
Name string
Expand Down
Loading