Skip to content

Commit

Permalink
MGMT-16649: Use new ignition endpoint HTTP headers when requesting Ig…
Browse files Browse the repository at this point in the history
…nition (openshift#5934)

* MGMT-16649: Update host with ignition headers from Agent CR

https://issues.redhat.com/browse/MGMT-16649
Adds the ignition headers from the Agent CR to the host

* MGMT-16649: Update ignition API to use new HTTP headers

https://issues.redhat.com/browse/MGMT-16649
Pass in the new headers when fetching ignition.

* MGMT-16649: Use ignition HTTP headers in host API_VIP check command

https://issues.redhat.com/browse/MGMT-16649
  • Loading branch information
CrystalChun authored and danmanor committed Feb 12, 2024
1 parent e093e19 commit add4501
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 23 deletions.
41 changes: 36 additions & 5 deletions internal/bminventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,7 @@ func (b *bareMetalInventory) V2ImportClusterInternal(ctx context.Context, kubeKe
return &newCluster, nil
}

func (b *bareMetalInventory) createAndUploadDay2NodeIgnition(ctx context.Context, cluster *common.Cluster, host *models.Host, ignitionEndpointToken string) error {
func (b *bareMetalInventory) createAndUploadDay2NodeIgnition(ctx context.Context, cluster *common.Cluster, host *models.Host, ignitionEndpointToken, ignitionEndpointHTTPHeaders string) error {
log := logutil.FromContext(ctx, b.log)
log.Infof("Starting createAndUploadDay2NodeIgnition for cluster %s, host %s", cluster.ID, host.ID)

Expand All @@ -996,7 +996,7 @@ func (b *bareMetalInventory) createAndUploadDay2NodeIgnition(ctx context.Context
caCert = cluster.IgnitionEndpoint.CaCertificate
}

fullIgnition, err := b.IgnitionBuilder.FormatSecondDayWorkerIgnitionFile(ignitionEndpointUrl, caCert, ignitionEndpointToken, host)
fullIgnition, err := b.IgnitionBuilder.FormatSecondDayWorkerIgnitionFile(ignitionEndpointUrl, caCert, ignitionEndpointToken, ignitionEndpointHTTPHeaders, host)
if err != nil {
return errors.Wrapf(err, "Failed to create ignition string for cluster %s, host %s", cluster.ID, host.ID)
}
Expand Down Expand Up @@ -1438,9 +1438,9 @@ func (b *bareMetalInventory) InstallSingleDay2HostInternal(ctx context.Context,
return err
}
// move host to installing
err = b.createAndUploadDay2NodeIgnition(ctx, cluster, &h.Host, h.IgnitionEndpointToken)
err = b.createAndUploadDay2NodeIgnition(ctx, cluster, &h.Host, h.IgnitionEndpointToken, h.IgnitionEndpointHTTPHeaders)
if err != nil {
log.Errorf("Failed to upload ignition for host %s", h.RequestedHostname)
log.WithError(err).Errorf("Failed to upload ignition for host %s", h.RequestedHostname)
return err
}
if installErr := b.hostApi.Install(ctx, &h.Host, tx); installErr != nil {
Expand Down Expand Up @@ -1509,7 +1509,7 @@ func (b *bareMetalInventory) V2InstallHost(ctx context.Context, params installer
if cluster, err = common.GetClusterFromDB(b.db, *h.ClusterID, common.SkipEagerLoading); err != nil {
return common.GenerateErrorResponder(err)
}
err = b.createAndUploadDay2NodeIgnition(ctx, cluster, h, host.IgnitionEndpointToken)
err = b.createAndUploadDay2NodeIgnition(ctx, cluster, h, host.IgnitionEndpointToken, host.IgnitionEndpointHTTPHeaders)
if err != nil {
log.Errorf("Failed to upload ignition for host %s", h.RequestedHostname)
return common.GenerateErrorResponder(err)
Expand Down Expand Up @@ -5977,6 +5977,11 @@ func (b *bareMetalInventory) V2UpdateHostInternal(ctx context.Context, params in
if err != nil {
return err
}
err = b.updateIgnitionEndpointHTTPHeaders(ctx, host, params.HostUpdateParams.IgnitionEndpointHTTPHeaders, tx)
if err != nil {
return err
}

err = b.updateNodeLabels(ctx, host, params.HostUpdateParams.NodeLabels, tx)
if err != nil {
return err
Expand Down Expand Up @@ -6127,6 +6132,32 @@ func (b *bareMetalInventory) updateHostIgnitionEndpointToken(ctx context.Context
return nil
}

func (b *bareMetalInventory) updateIgnitionEndpointHTTPHeaders(ctx context.Context, host *common.Host, ignitionEndpointHTTPHeadersList []*models.IgnitionEndpointHTTPHeadersParams, db *gorm.DB) error {
log := logutil.FromContext(ctx, b.log)
if ignitionEndpointHTTPHeadersList == nil {
log.Infof("No request for ignition endpoint HTTP Headers update for host %s", host.ID)
return nil
}

ignitionEndpointHTTPHeaders := make(map[string]string)
for _, hdr := range ignitionEndpointHTTPHeadersList {
ignitionEndpointHTTPHeaders[*hdr.Key] = *hdr.Value
}

ignitionEndpointHTTPHeadersStr, err := json.Marshal(ignitionEndpointHTTPHeaders)
if err != nil {
return common.NewApiError(http.StatusBadRequest, errors.Wrapf(err, "failed to marshal ignition endpoint HTTP Headers for host %s", host.ID))
}

err = b.hostApi.UpdateIgnitionEndpointHTTPHeaders(ctx, &host.Host, string(ignitionEndpointHTTPHeadersStr), db)
if err != nil {
log.WithError(err).Errorf("failed to set ignition endpoint HTTP Headers <%s> host <%s>, infra env <%s>",
ignitionEndpointHTTPHeadersStr, host.ID, host.InfraEnvID)
return common.NewApiError(http.StatusInternalServerError, err)
}
return nil
}

func (b *bareMetalInventory) updateNodeLabels(ctx context.Context, host *common.Host, nodeLabelsList []*models.NodeLabelParams, db *gorm.DB) error {
log := logutil.FromContext(ctx, b.log)
if nodeLabelsList == nil {
Expand Down
13 changes: 7 additions & 6 deletions internal/bminventory/inventory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10323,6 +10323,7 @@ var _ = Describe("infraEnvs host", func() {
mockHostApi.EXPECT().UpdateInstallationDisk(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
mockHostApi.EXPECT().UpdateMachineConfigPoolName(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
mockHostApi.EXPECT().UpdateIgnitionEndpointToken(gomock.Any(), gomock.Any(), gomock.Any(), "mytoken").Return(nil).Times(1)
mockHostApi.EXPECT().UpdateIgnitionEndpointHTTPHeaders(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(0)
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockClusterApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).Times(1)
mockHostApi.EXPECT().GetStagesByRole(gomock.Any(), gomock.Any()).Return(nil).Times(1)
Expand Down Expand Up @@ -12794,7 +12795,7 @@ var _ = Describe("Install Host test", func() {
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockHostApi.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
res := bm.V2InstallHost(ctx, params)
Expect(res).Should(BeAssignableToTypeOf(installer.NewV2InstallHostAccepted()))
})
Expand All @@ -12812,7 +12813,7 @@ var _ = Describe("Install Host test", func() {
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockHostApi.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile("http://example.com/worker", gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile("http://example.com/worker", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
res := bm.V2InstallHost(ctx, params)
Expect(res).Should(BeAssignableToTypeOf(installer.NewV2InstallHostAccepted()))
})
Expand Down Expand Up @@ -12860,7 +12861,7 @@ var _ = Describe("Install Host test", func() {
mockHostApi.EXPECT().AutoAssignRole(gomock.Any(), gomock.Any(), gomock.Any()).Return(true, nil).Times(1)
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("some error")).Times(0)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("ign failure")).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("ign failure")).Times(1)
res := bm.V2InstallHost(ctx, params)
verifyApiError(res, http.StatusInternalServerError)
})
Expand All @@ -12875,7 +12876,7 @@ var _ = Describe("Install Host test", func() {
mockHostApi.EXPECT().AutoAssignRole(gomock.Any(), gomock.Any(), gomock.Any()).Return(true, nil).Times(1)
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("some error")).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
res := bm.V2InstallHost(ctx, params)
verifyApiError(res, http.StatusInternalServerError)
})
Expand Down Expand Up @@ -12924,7 +12925,7 @@ var _ = Describe("InstallSingleDay2Host test", func() {
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockHostApi.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
res := bm.InstallSingleDay2HostInternal(ctx, clusterID, clusterID, hostId)
Expect(res).Should(BeNil())
})
Expand All @@ -12937,7 +12938,7 @@ var _ = Describe("InstallSingleDay2Host test", func() {
mockHostApi.EXPECT().RefreshStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockHostApi.EXPECT().Install(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New(expectedErrMsg)).Times(1)
mockS3Client.EXPECT().Upload(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
mockIgnitionBuilder.EXPECT().FormatSecondDayWorkerIgnitionFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(secondDayWorkerIgnition, nil).Times(1)
res := bm.InstallSingleDay2HostInternal(ctx, clusterID, clusterID, hostId)
Expect(res.Error()).Should(Equal(expectedErrMsg))
})
Expand Down
27 changes: 27 additions & 0 deletions internal/controller/controllers/agent_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,33 @@ func (r *AgentReconciler) updateIfNeeded(ctx context.Context, log logrus.FieldLo
}
}

if agent.Spec.IgnitionEndpointHTTPHeaders != nil {
hostIgnitionEndpointHTTPHeaders := make(map[string]string)
if internalHost.IgnitionEndpointHTTPHeaders != "" {
if err = json.Unmarshal([]byte(internalHost.IgnitionEndpointHTTPHeaders), &hostIgnitionEndpointHTTPHeaders); err != nil {
log.WithError(err).Errorf("failed to unmarshal ignition endpoint HTTP headers for host %s infra-env %s", internalHost.ID.String(), internalHost.InfraEnvID.String())
}
}

if !reflect.DeepEqual(agent.Spec.IgnitionEndpointHTTPHeaders, hostIgnitionEndpointHTTPHeaders) {
funk.ForEach(agent.Spec.IgnitionEndpointHTTPHeaders, func(key, value string) {
params.HostUpdateParams.IgnitionEndpointHTTPHeaders = append(params.HostUpdateParams.IgnitionEndpointHTTPHeaders, &models.IgnitionEndpointHTTPHeadersParams{
Key: swag.String(key),
Value: swag.String(value),
})
})
hostUpdate = true
}
} else {
if internalHost.IgnitionEndpointHTTPHeaders != "" {
hostUpdate = true
params.HostUpdateParams.IgnitionEndpointHTTPHeaders = append(params.HostUpdateParams.IgnitionEndpointHTTPHeaders, &models.IgnitionEndpointHTTPHeadersParams{
Key: swag.String(""),
Value: swag.String(""),
})
}
}

if hostUpdate {
var hostStatusesBeforeInstallationOrUnbound = []string{
models.HostStatusDiscovering, models.HostStatusKnown, models.HostStatusDisconnected,
Expand Down
17 changes: 17 additions & 0 deletions internal/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ type API interface {
UpdateNTP(ctx context.Context, h *models.Host, ntpSources []*models.NtpSource, db *gorm.DB) error
UpdateMachineConfigPoolName(ctx context.Context, db *gorm.DB, h *models.Host, machineConfigPoolName string) error
UpdateIgnitionEndpointToken(ctx context.Context, db *gorm.DB, h *models.Host, token string) error
UpdateIgnitionEndpointHTTPHeaders(ctx context.Context, h *models.Host, nodeLabelsStr string, db *gorm.DB) error
UpdateNodeLabels(ctx context.Context, h *models.Host, nodeLabelsStr string, db *gorm.DB) error
UpdateNodeSkipDiskFormatting(ctx context.Context, h *models.Host, skipDiskFormatting string, db *gorm.DB) error
UpdateInstallationDisk(ctx context.Context, db *gorm.DB, h *models.Host, installationDiskId string) error
Expand Down Expand Up @@ -809,6 +810,22 @@ func (m *Manager) UpdateIgnitionEndpointToken(ctx context.Context, db *gorm.DB,
return m.updateHost(ctx, cdb, h, updates).Error
}

func (m *Manager) UpdateIgnitionEndpointHTTPHeaders(ctx context.Context, h *models.Host, nodeLabelsStr string, db *gorm.DB) error {
hostStatus := swag.StringValue(h.Status)
if !funk.ContainsString(hostStatusesBeforeInstallationOrUnbound[:], hostStatus) {
return common.NewApiError(http.StatusBadRequest,
errors.Errorf("Host is in %s state, ignition endpoint HTTP headers can be set only in one of %s states",
hostStatus, hostStatusesBeforeInstallation[:]))
}

cdb := m.db
if db != nil {
cdb = db
}
updates := map[string]interface{}{"ignition_endpoint_http_headers": nodeLabelsStr, "trigger_monitor_timestamp": time.Now()}
return m.updateHost(ctx, cdb, h, updates).Error
}

func (m *Manager) UpdateNodeLabels(ctx context.Context, h *models.Host, nodeLabelsStr string, db *gorm.DB) error {
hostStatus := swag.StringValue(h.Status)
if !funk.ContainsString(hostStatusesBeforeInstallationOrUnbound[:], hostStatus) {
Expand Down
8 changes: 8 additions & 0 deletions internal/host/hostcommands/api_vip_connectivity_check_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ func (c *apivipConnectivityCheckCmd) GetSteps(ctx context.Context, host *models.
requestHeaders = append(requestHeaders, &models.APIVipConnectivityAdditionalRequestHeader{Key: "Authorization", Value: fmt.Sprintf("Bearer %s", commonHost.IgnitionEndpointToken)})
request.IgnitionEndpointToken = &commonHost.IgnitionEndpointToken
}
if commonHost.IgnitionEndpointHTTPHeaders != "" {
additionalHeaders := make(map[string]string)
if err = json.Unmarshal([]byte(commonHost.IgnitionEndpointHTTPHeaders), &additionalHeaders); err == nil { //nolint:errcheck // errors adding additional headers shouldn't prevent the request from being sent
for k, v := range additionalHeaders {
requestHeaders = append(requestHeaders, &models.APIVipConnectivityAdditionalRequestHeader{Key: k, Value: v})
}
}
}
request.RequestHeaders = requestHeaders

requestBytes, err := json.Marshal(request)
Expand Down
14 changes: 14 additions & 0 deletions internal/host/mock_host_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions internal/ignition/ignition.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ type Generator interface {
//go:generate mockgen -source=ignition.go -package=ignition -destination=mock_ignition.go
type IgnitionBuilder interface {
FormatDiscoveryIgnitionFile(ctx context.Context, infraEnv *common.InfraEnv, cfg IgnitionConfig, safeForLogs bool, authType auth.AuthType, overrideDiscoveryISOType string) (string, error)
FormatSecondDayWorkerIgnitionFile(url string, caCert *string, bearerToken string, host *models.Host) ([]byte, error)
FormatSecondDayWorkerIgnitionFile(url string, caCert *string, bearerToken, ignitionEndpointHTTPHeaders string, host *models.Host) ([]byte, error)
}

type installerGenerator struct {
Expand Down Expand Up @@ -1906,7 +1906,7 @@ func (ib *ignitionBuilder) prepareStaticNetworkConfigForIgnition(ctx context.Con
return filesList, nil
}

func (ib *ignitionBuilder) FormatSecondDayWorkerIgnitionFile(url string, caCert *string, bearerToken string, host *models.Host) ([]byte, error) {
func (ib *ignitionBuilder) FormatSecondDayWorkerIgnitionFile(url string, caCert *string, bearerToken, ignitionEndpointHTTPHeaders string, host *models.Host) ([]byte, error) {
var ignitionParams = map[string]interface{}{
// https://github.com/openshift/machine-config-operator/blob/master/docs/MachineConfigServer.md#endpoint
"SOURCE": url,
Expand All @@ -1917,6 +1917,17 @@ func (ib *ignitionBuilder) FormatSecondDayWorkerIgnitionFile(url string, caCert
ignitionParams["HEADERS"].(map[string]string)["Authorization"] = fmt.Sprintf("Bearer %s", bearerToken)
}

if ignitionEndpointHTTPHeaders != "" {
additionalHeaders := make(map[string]string)
if err := json.Unmarshal([]byte(ignitionEndpointHTTPHeaders), &additionalHeaders); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal ignition endpoint HTTP headers for host %s", host.ID)
}
for k, v := range additionalHeaders {
ignitionParams["HEADERS"].(map[string]string)[k] = v
}

}

if caCert != nil {
ignitionParams["CACERT"] = fmt.Sprintf("data:text/plain;base64,%s", *caCert)
}
Expand Down

0 comments on commit add4501

Please sign in to comment.