Skip to content

Commit f5efa52

Browse files
Merge pull request #9957 from CrystalChun/use-clusterimageset
MGMT-23339: Use release image URL from ClusterImageSet for KubeAPI clusters
2 parents ca6968f + 9531cb0 commit f5efa52

File tree

5 files changed

+235
-169
lines changed

5 files changed

+235
-169
lines changed

cmd/agentbasedinstaller/register.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func RegisterCluster(ctx context.Context, log *log.Logger, bmInventory *client.A
105105
operatorInfo = operatorsToArray(operatorList)
106106
}
107107

108-
clusterParams := controllers.CreateClusterParams(&cd, &aci, pullSecret, releaseImageVersion, releaseImageCPUArch, nil, operatorInfo)
108+
clusterParams := controllers.CreateClusterParams(&cd, &aci, pullSecret, &models.ReleaseImage{Version: &releaseImageVersion, CPUArchitecture: &releaseImageCPUArch, URL: &releaseImage}, nil, operatorInfo)
109109

110110
if aci.Spec.Networking.NetworkType != "" {
111111
clusterParams.NetworkType = &aci.Spec.Networking.NetworkType

internal/bminventory/inventory.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -564,11 +564,9 @@ func (b *bareMetalInventory) getOLMMonitoredOperators(log *logrus.Entry, cluster
564564
}
565565

566566
func (b *bareMetalInventory) getNewClusterReleaseImage(ctx context.Context, params *models.ClusterCreateParams, arch string) (*models.ReleaseImage, error) {
567-
releaseImage, err := b.versionsHandler.GetReleaseImage(ctx, swag.StringValue(params.OpenshiftVersion),
568-
arch, swag.StringValue(params.PullSecret))
567+
releaseImage, err := b.getReleaseImage(ctx, params, arch)
569568
if err != nil {
570-
return nil, errors.Wrapf(err, "Openshift version %s for CPU architecture %s is not supported",
571-
swag.StringValue(params.OpenshiftVersion), arch)
569+
return nil, err
572570
}
573571

574572
if !b.EnableImageService {
@@ -590,6 +588,37 @@ func (b *bareMetalInventory) getNewClusterReleaseImage(ctx context.Context, para
590588
return releaseImage, nil
591589
}
592590

591+
func (b *bareMetalInventory) getReleaseImage(ctx context.Context, params *models.ClusterCreateParams, arch string) (*models.ReleaseImage, error) {
592+
if params.OcpReleaseImage != "" {
593+
releaseImage, err := b.versionsHandler.GetReleaseImageByURL(ctx, params.OcpReleaseImage, swag.StringValue(params.PullSecret))
594+
if err != nil {
595+
return nil, errors.Wrapf(err, "failed to get release image by URL %s during cluster registration", params.OcpReleaseImage)
596+
}
597+
598+
normalizedArch := common.NormalizeCPUArchitecture(arch)
599+
if normalizedArch == common.MultiCPUArchitecture {
600+
if len(releaseImage.CPUArchitectures) <= 1 {
601+
return nil, errors.Errorf("release image URL %s does not support requested CPU architecture %s", params.OcpReleaseImage, normalizedArch)
602+
}
603+
return releaseImage, nil
604+
}
605+
for _, cpuArch := range releaseImage.CPUArchitectures {
606+
normalizedCpuArch := common.NormalizeCPUArchitecture(cpuArch)
607+
if normalizedCpuArch == normalizedArch {
608+
return releaseImage, nil
609+
}
610+
}
611+
return nil, errors.Errorf("Requested CPU architecture %s is not supported for release image URL %s. Supported architectures in release image: %v", normalizedArch, params.OcpReleaseImage, releaseImage.CPUArchitectures)
612+
}
613+
614+
releaseImage, err := b.versionsHandler.GetReleaseImage(ctx, swag.StringValue(params.OpenshiftVersion), arch, swag.StringValue(params.PullSecret))
615+
if err != nil {
616+
return nil, errors.Wrapf(err, "Openshift version %s for CPU architecture %s is not supported",
617+
swag.StringValue(params.OpenshiftVersion), arch)
618+
}
619+
return releaseImage, nil
620+
}
621+
593622
func MarshalNewClusterParamsNoPullSecret(params installer.V2RegisterClusterParams, log *logrus.Entry) []byte {
594623
// Masked the Pull Secret to prevent its value from being displayed in the logs.
595624
clusterParamsNoPullSecret := *params.NewClusterParams

internal/bminventory/inventory_test.go

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,17 +265,30 @@ func toMac(macStr string) *strfmt.MAC {
265265
return &mac
266266
}
267267

268-
func mockClusterRegisterSteps() {
268+
func mockClusterRegisterSteps(withReleaseImageURL bool) {
269269
mockSecretValidator.EXPECT().ValidatePullSecret(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
270-
mockVersions.EXPECT().GetReleaseImage(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(common.TestDefaultConfig.ReleaseImage, nil).Times(1)
270+
if withReleaseImageURL {
271+
mockVersions.EXPECT().GetReleaseImageByURL(gomock.Any(), gomock.Any(), gomock.Any()).Return(common.TestDefaultConfig.ReleaseImage, nil).Times(1)
272+
} else {
273+
mockVersions.EXPECT().GetReleaseImage(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(common.TestDefaultConfig.ReleaseImage, nil).Times(1)
274+
}
271275

272276
mockOSImages.EXPECT().GetOsImage(gomock.Any(), gomock.Any()).Return(common.TestDefaultConfig.OsImage, nil).Times(1)
273277
mockOperatorManager.EXPECT().GetSupportedOperatorsByType(models.OperatorTypeBuiltin).Return([]*models.MonitoredOperator{&common.TestDefaultConfig.MonitoredOperator}).Times(1)
274278
mockProviderRegistry.EXPECT().SetPlatformUsages(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
275279
}
276280

277281
func mockClusterRegisterSuccess(withEvents bool) {
278-
mockClusterRegisterSteps()
282+
mockClusterRegisterSteps(false)
283+
mockMetric.EXPECT().ClusterRegistered().Times(1)
284+
285+
if withEvents {
286+
mockEvents.EXPECT().SendClusterEvent(gomock.Any(), eventstest.NewEventMatcher(
287+
eventstest.WithNameMatcher(eventgen.ClusterRegistrationSucceededEventName))).Times(1)
288+
}
289+
}
290+
func mockClusterRegisterSuccessWithReleaseImageURL(withEvents bool) {
291+
mockClusterRegisterSteps(true)
279292
mockMetric.EXPECT().ClusterRegistered().Times(1)
280293

281294
if withEvents {
@@ -14676,7 +14689,7 @@ var _ = Describe("RegisterCluster", func() {
1467614689
})
1467714690
Context("Pull secret validation", func() {
1467814691
It("Successfully validates the pull secret if it does not contain auth for a mirrored registry", func() {
14679-
mockClusterRegisterSuccess(true)
14692+
mockClusterRegisterSuccessWithReleaseImageURL(true)
1468014693
mockAMSSubscription(ctx)
1468114694
conf, _ := getMirrorRegistryConfigurations(getSecureRegistryToml("fake-registry.example.com", mirrorRegistry), mirrorRegistryCertificate)
1468214695
params := getClusterCreateParams()
@@ -16373,7 +16386,7 @@ var _ = Describe("RegisterCluster", func() {
1637316386
It("cluster api failed to register", func() {
1637416387
bm.clusterApi = mockClusterApi
1637516388
mockClusterApi.EXPECT().RegisterCluster(ctx, gomock.Any()).Return(errors.Errorf("error")).Times(1)
16376-
mockClusterRegisterSteps()
16389+
mockClusterRegisterSteps(false)
1637716390

1637816391
reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{
1637916392
NewClusterParams: getDefaultClusterCreateParams(),
@@ -16433,6 +16446,20 @@ var _ = Describe("RegisterCluster", func() {
1643316446
Expect(actual.Payload.OcpReleaseImage).To(Equal(common.TestDefaultConfig.ReleaseImageUrl))
1643416447
})
1643516448

16449+
It("successfully registers cluster with openshift release image URL defined in the cluster create params", func() {
16450+
mockClusterRegisterSuccessWithReleaseImageURL(true)
16451+
mockAMSSubscription(ctx)
16452+
clusterParams := getDefaultClusterCreateParams()
16453+
clusterParams.OcpReleaseImage = common.TestDefaultConfig.ReleaseImageUrl
16454+
reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{
16455+
NewClusterParams: clusterParams,
16456+
})
16457+
Expect(reflect.TypeOf(reply)).Should(Equal(reflect.TypeOf(installer.NewV2RegisterClusterCreated())))
16458+
actual := reply.(*installer.V2RegisterClusterCreated)
16459+
Expect(actual.Payload.OpenshiftVersion).To(Equal(common.TestDefaultConfig.ReleaseVersion))
16460+
Expect(actual.Payload.OcpReleaseImage).To(Equal(common.TestDefaultConfig.ReleaseImageUrl))
16461+
})
16462+
1643616463
It("Register cluster with default CPU architecture", func() {
1643716464
mockClusterRegisterSuccess(true)
1643816465
mockAMSSubscription(ctx)
@@ -16531,6 +16558,38 @@ var _ = Describe("RegisterCluster", func() {
1653116558
actual := reply.(*installer.V2RegisterClusterCreated)
1653216559
Expect(actual.Payload.CPUArchitecture).To(Equal(common.TestDefaultConfig.CPUArchitecture))
1653316560
})
16561+
Context("Register with release image URL", func() {
16562+
It("should register cluster with release image URL successfully", func() {
16563+
mockClusterRegisterSuccessWithReleaseImageURL(true)
16564+
mockAMSSubscription(ctx)
16565+
clusterParams := getDefaultClusterCreateParams()
16566+
clusterParams.OcpReleaseImage = common.TestDefaultConfig.ReleaseImageUrl
16567+
reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{
16568+
NewClusterParams: clusterParams,
16569+
})
16570+
Expect(reflect.TypeOf(reply)).Should(Equal(reflect.TypeOf(installer.NewV2RegisterClusterCreated())))
16571+
actual := reply.(*installer.V2RegisterClusterCreated)
16572+
Expect(actual.Payload.OpenshiftVersion).To(Equal(common.TestDefaultConfig.ReleaseVersion))
16573+
Expect(actual.Payload.OcpReleaseImage).To(Equal(common.TestDefaultConfig.ReleaseImageUrl))
16574+
})
16575+
16576+
It("should fail if openshift release image is different from cluster architecture", func() {
16577+
mockVersions.EXPECT().GetReleaseImageByURL(gomock.Any(), gomock.Any(), gomock.Any()).Return(&models.ReleaseImage{
16578+
CPUArchitecture: swag.String(common.ARM64CPUArchitecture),
16579+
CPUArchitectures: []string{common.ARM64CPUArchitecture},
16580+
OpenshiftVersion: swag.String("4.12.0"),
16581+
URL: swag.String(common.TestDefaultConfig.ReleaseImageUrl),
16582+
Version: swag.String("4.12.0"),
16583+
}, nil).Times(1)
16584+
16585+
clusterParams := getDefaultClusterCreateParams()
16586+
clusterParams.OcpReleaseImage = common.TestDefaultConfig.ReleaseImageUrl
16587+
reply := bm.V2RegisterCluster(ctx, installer.V2RegisterClusterParams{
16588+
NewClusterParams: clusterParams,
16589+
})
16590+
Expect(reply).Should(BeAssignableToTypeOf(common.NewApiError(http.StatusBadRequest, errors.Errorf("Requested CPU architecture %s is not supported for release image URL %s. Supported architectures in release image: %v", common.ARM64CPUArchitecture, common.TestDefaultConfig.ReleaseImageUrl, []string{common.ARM64CPUArchitecture}))))
16591+
})
16592+
})
1653416593

1653516594
Context("Cluster Tags", func() {
1653616595
It("Register cluster with tags", func() {
@@ -16649,7 +16708,7 @@ var _ = Describe("RegisterCluster", func() {
1664916708
payload.Username = userName1
1665016709
payload.Organization = orgID1
1665116710
authCtx = context.WithValue(ctx, restapi.AuthKey, payload)
16652-
mockClusterRegisterSteps()
16711+
mockClusterRegisterSteps(false)
1665316712
mockUsageReports()
1665416713
mockAMSSubscription(authCtx)
1665516714
mockMetric.EXPECT().ClusterRegistered().Times(1)
@@ -17512,7 +17571,7 @@ var _ = Describe("AMS subscriptions", func() {
1751217571
It("register cluster - deregister if we failed to create AMS subscription", func() {
1751317572
bm.clusterApi = mockClusterApi
1751417573
mockClusterApi.EXPECT().RegisterCluster(ctx, gomock.Any()).Return(nil)
17515-
mockClusterRegisterSteps()
17574+
mockClusterRegisterSteps(false)
1751617575
mockAccountsMgmt.EXPECT().CreateSubscription(ctx, gomock.Any(), clusterName).Return(nil, errors.New("dummy"))
1751717576
mockClusterApi.EXPECT().DeregisterCluster(ctx, gomock.Any())
1751817577

@@ -17527,7 +17586,7 @@ var _ = Describe("AMS subscriptions", func() {
1752717586
It("register cluster - delete AMS subscription if we failed to patch DB with ams_subscription_id", func() {
1752817587
bm.clusterApi = mockClusterApi
1752917588
mockClusterApi.EXPECT().RegisterCluster(ctx, gomock.Any()).Return(nil)
17530-
mockClusterRegisterSteps()
17589+
mockClusterRegisterSteps(false)
1753117590
mockAMSSubscription(ctx)
1753217591
mockClusterApi.EXPECT().UpdateAmsSubscriptionID(ctx, gomock.Any(), strfmt.UUID("")).Return(common.NewApiError(http.StatusInternalServerError, errors.New("dummy")))
1753317592
mockClusterApi.EXPECT().DeregisterCluster(ctx, gomock.Any())

internal/controller/controllers/clusterdeployments_controller.go

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,10 @@ func (r *ClusterDeploymentsReconciler) Reconcile(origCtx context.Context, req ct
212212
log.WithError(err).Errorf("failed to get pull secret for cluster deployment %s", clusterDeployment.Name)
213213
return r.updateStatus(ctx, log, clusterInstall, clusterDeployment, nil, err)
214214
}
215-
releaseImage, err := r.getReleaseImage(ctx, clusterDeployment, clusterInstall, pullSecret)
215+
216+
clusterImageSet, err := r.ensureClusterImageSetRef(ctx, log, clusterDeployment, clusterInstall)
216217
if err != nil {
217-
log.WithError(err).Errorf("failed to get release image for cluster %s, ensure AgentClusterInstall.Spec.ImageSetRef references a ClusterImageSet", clusterDeployment.Name)
218+
log.WithError(err).Errorf("failed to ensure release image for cluster %s, AgentClusterInstall.Spec.ImageSetRef must reference a valid ClusterImageSet", clusterDeployment.Name)
218219
return r.updateStatus(ctx, log, clusterInstall, clusterDeployment, nil, err)
219220
}
220221

@@ -229,6 +230,11 @@ func (r *ClusterDeploymentsReconciler) Reconcile(origCtx context.Context, req ct
229230

230231
cluster, err := r.Installer.GetClusterByKubeKey(req.NamespacedName)
231232
if errors.Is(err, gorm.ErrRecordNotFound) {
233+
releaseImage, releaseImageErr := r.getReleaseImage(ctx, clusterImageSet, pullSecret)
234+
if releaseImageErr != nil {
235+
log.WithError(releaseImageErr).Errorf("failed to get release image for cluster %s", clusterDeployment.Name)
236+
return r.updateStatus(ctx, log, clusterInstall, clusterDeployment, nil, releaseImageErr)
237+
}
232238
if !isInstalled(clusterDeployment, clusterInstall) {
233239
return r.createNewCluster(ctx, log, req.NamespacedName, pullSecret, releaseImage, clusterDeployment, clusterInstall, mirrorRegistryConfiguration)
234240
}
@@ -1378,25 +1384,26 @@ func (r *ClusterDeploymentsReconciler) addCustomManifests(ctx context.Context, l
13781384
}
13791385

13801386
func CreateClusterParams(clusterDeployment *hivev1.ClusterDeployment, clusterInstall *hiveext.AgentClusterInstall,
1381-
pullSecret string, releaseImageVersion string, releaseImageCPUArch string,
1387+
pullSecret string, releaseImage *models.ReleaseImage,
13821388
ignitionEndpoint *models.IgnitionEndpoint, olmOperators []*models.OperatorCreateParams) *models.ClusterCreateParams {
13831389
spec := clusterDeployment.Spec
13841390
platform, _ := getPlatform(clusterInstall.Spec)
13851391

13861392
clusterParams := &models.ClusterCreateParams{
13871393
BaseDNSDomain: spec.BaseDomain,
13881394
Name: swag.String(spec.ClusterName),
1389-
OpenshiftVersion: &releaseImageVersion,
1395+
OpenshiftVersion: releaseImage.Version,
13901396
PullSecret: swag.String(pullSecret),
13911397
VipDhcpAllocation: swag.Bool(false),
13921398
APIVips: ApiVipsEntriesToArray(clusterInstall.Spec.APIVIPs),
13931399
IngressVips: IngressVipsEntriesToArray(clusterInstall.Spec.IngressVIPs),
13941400
SSHPublicKey: clusterInstall.Spec.SSHPublicKey,
1395-
CPUArchitecture: releaseImageCPUArch,
1401+
CPUArchitecture: *releaseImage.CPUArchitecture,
13961402
UserManagedNetworking: swag.Bool(isUserManagedNetwork(clusterInstall)),
13971403
Platform: platform,
13981404
SchedulableMasters: swag.Bool(clusterInstall.Spec.MastersSchedulable),
13991405
ControlPlaneCount: swag.Int64(int64(clusterInstall.Spec.ProvisionRequirements.ControlPlaneAgents)),
1406+
OcpReleaseImage: *releaseImage.URL,
14001407
}
14011408

14021409
if len(clusterInstall.Spec.Networking.ClusterNetwork) > 0 {
@@ -1484,7 +1491,10 @@ func (r *ClusterDeploymentsReconciler) createNewCluster(
14841491
mirrorRegistryConfiguration *common.MirrorRegistryConfiguration) (ctrl.Result, error) {
14851492

14861493
log.Infof("Creating a new cluster %s %s", clusterDeployment.Name, clusterDeployment.Namespace)
1487-
1494+
if releaseImage == nil { // releaseImage shoudn't be nil, but guard against it just in case
1495+
log.Errorf("release image cannot be nil for day 1 cluster %s %s", clusterDeployment.Name, clusterDeployment.Namespace)
1496+
return r.updateStatus(ctx, log, clusterInstall, clusterDeployment, nil, fmt.Errorf("release image cannot be nil for day 1 cluster %s %s. Ensure AgentClusterInstall.Spec.ImageSetRef references a valid ClusterImageSet", clusterDeployment.Name, clusterDeployment.Namespace))
1497+
}
14881498
var ignitionEndpoint *models.IgnitionEndpoint
14891499
var err error
14901500
if clusterInstall.Spec.IgnitionEndpoint != nil {
@@ -1495,8 +1505,7 @@ func (r *ClusterDeploymentsReconciler) createNewCluster(
14951505
}
14961506
}
14971507

1498-
clusterParams := CreateClusterParams(clusterDeployment, clusterInstall, pullSecret, *releaseImage.Version,
1499-
*releaseImage.CPUArchitecture, ignitionEndpoint, nil)
1508+
clusterParams := CreateClusterParams(clusterDeployment, clusterInstall, pullSecret, releaseImage, ignitionEndpoint, nil)
15001509

15011510
c, err := r.Installer.RegisterClusterInternal(ctx, &key, mirrorRegistryConfiguration, installer.V2RegisterClusterParams{
15021511
NewClusterParams: clusterParams,
@@ -1555,12 +1564,7 @@ func (r *ClusterDeploymentsReconciler) createNewDay2Cluster(
15551564
return r.updateStatus(ctx, log, clusterInstall, clusterDeployment, c, err)
15561565
}
15571566

1558-
func (r *ClusterDeploymentsReconciler) getReleaseImage(
1559-
ctx context.Context,
1560-
clusterDeployment *hivev1.ClusterDeployment,
1561-
clusterInstall *hiveext.AgentClusterInstall,
1562-
pullSecret string) (*models.ReleaseImage, error) {
1563-
1567+
func (r *ClusterDeploymentsReconciler) ensureClusterImageSetRef(ctx context.Context, log logrus.FieldLogger, clusterDeployment *hivev1.ClusterDeployment, clusterInstall *hiveext.AgentClusterInstall) (*hivev1.ClusterImageSet, error) {
15641568
// Make sure that the ImageSetRef is set before continuing
15651569
if clusterInstall.Spec.ImageSetRef == nil {
15661570
// ImageSetRef is not required for already installed clusters
@@ -1577,9 +1581,23 @@ func (r *ClusterDeploymentsReconciler) getReleaseImage(
15771581
Name: clusterInstall.Spec.ImageSetRef.Name,
15781582
}
15791583
if err := r.Client.Get(ctx, key, clusterImageSet); err != nil {
1584+
if k8serrors.IsNotFound(err) {
1585+
return nil, errors.Errorf("ClusterImageSet %s not found. Please ensure the ClusterImageSet exists", key.Name)
1586+
}
15801587
return nil, errors.Wrapf(err, "failed to get cluster image set %s", key.Name)
15811588
}
15821589

1590+
return clusterImageSet, nil
1591+
}
1592+
1593+
func (r *ClusterDeploymentsReconciler) getReleaseImage(
1594+
ctx context.Context,
1595+
clusterImageSet *hivev1.ClusterImageSet,
1596+
pullSecret string) (*models.ReleaseImage, error) {
1597+
if clusterImageSet == nil {
1598+
r.Log.Debugf("cluster image set is nil for cluster")
1599+
return nil, nil
1600+
}
15831601
releaseImage, err := r.VersionsHandler.GetReleaseImageByURL(ctx, clusterImageSet.Spec.ReleaseImage, pullSecret)
15841602
if err != nil {
15851603
errMsgSuffix := ""
@@ -1592,7 +1610,7 @@ func (r *ClusterDeploymentsReconciler) getReleaseImage(
15921610
}
15931611
}
15941612
errMsg := "failed to get release image '%s'. Please ensure the releaseImage field in ClusterImageSet '%s' is valid, %s (error: %s)."
1595-
return nil, errors.New(fmt.Sprintf(errMsg, clusterImageSet.Spec.ReleaseImage, key.Name, errMsgSuffix, err.Error()))
1613+
return nil, errors.New(fmt.Sprintf(errMsg, clusterImageSet.Spec.ReleaseImage, clusterImageSet.Name, errMsgSuffix, err.Error()))
15961614
}
15971615

15981616
return releaseImage, nil

0 commit comments

Comments
 (0)