Skip to content

Commit

Permalink
Image Controller should be able to update component with created imag…
Browse files Browse the repository at this point in the history
…e repository (#90)

STONEBLD-1928

Signed-off-by: Robert Cerven <[email protected]>
  • Loading branch information
rcerven authored Jan 25, 2024
1 parent ca8c2a6 commit c5c0fae
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 11 deletions.
39 changes: 38 additions & 1 deletion controllers/imagerepository_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const (
ImageRepositoryFinalizer = "appstudio.openshift.io/image-repository"

buildPipelineServiceAccountName = "appstudio-pipeline"
updateComponentAnnotationName = "image-controller.appstudio.redhat.com/update-component-image"

metricsNamespace = "redhat_appstudio"
metricsSubsystem = "imagecontroller"
Expand Down Expand Up @@ -150,7 +151,7 @@ func (r *ImageRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Requ
log.Error(err, "failed to get image repository", l.Action, l.ActionView)
return ctrl.Result{}, err
}

repositoryIdForMetrics := fmt.Sprintf("%s=%s", imageRepository.Name, imageRepository.Namespace)

if !imageRepository.DeletionTimestamp.IsZero() {
Expand Down Expand Up @@ -199,6 +200,41 @@ func (r *ImageRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Requ
return ctrl.Result{}, nil
}

// Update component
if isComponentLinked(imageRepository) {
updateComponentAnnotation, updateComponentAnnotationExists := imageRepository.Annotations[updateComponentAnnotationName]
if updateComponentAnnotationExists && updateComponentAnnotation == "true" {

componentName := imageRepository.Labels[ComponentNameLabelName]
component := &appstudioredhatcomv1alpha1.Component{}
componentKey := types.NamespacedName{Namespace: imageRepository.Namespace, Name: componentName}
if err := r.Client.Get(ctx, componentKey, component); err != nil {
if errors.IsNotFound(err) {
log.Info("attempt to update non existing component", "ComponentName", componentName)
return ctrl.Result{}, nil
}

log.Error(err, "failed to get component", "ComponentName", componentName)
return ctrl.Result{}, err
}

component.Spec.ContainerImage = imageRepository.Status.Image.URL

if err := r.Client.Update(ctx, component); err != nil {
log.Error(err, "failed to update Component after provision", "ComponentName", componentName)
return ctrl.Result{}, err
}
log.Info("Updated component's ContainerImage", "ComponentName", componentName)
delete(imageRepository.Annotations, updateComponentAnnotationName)

if err := r.Client.Update(ctx, imageRepository); err != nil {
log.Error(err, "failed to update imageRepository annotation")
return ctrl.Result{}, err
}
log.Info("Updated image repository annotation")
}
}

if imageRepository.Status.State != imagerepositoryv1alpha1.ImageRepositoryStateReady {
return ctrl.Result{}, nil
}
Expand Down Expand Up @@ -358,6 +394,7 @@ func (r *ImageRepositoryReconciler) ProvisionImageRepository(ctx context.Context
// Do not brake provision because of failed owner reference
}
}

if err := r.Client.Update(ctx, imageRepository); err != nil {
log.Error(err, "failed to update CR after provision")
return err
Expand Down
48 changes: 41 additions & 7 deletions controllers/imagerepository_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,16 @@ var _ = Describe("Image repository controller", func() {
})

Context("Image repository for component provision", func() {
componentKey := types.NamespacedName{Name: defaultComponentName, Namespace: defaultNamespace}

BeforeEach(func() {
ResetTestQuayClientToFails()
deleteUploadSecrets(defaultNamespace)
createComponent(componentConfig{})
})

AfterEach(func() {
deleteComponent(componentKey)
})

It("should prepare environment", func() {
Expand All @@ -268,10 +274,9 @@ var _ = Describe("Image repository controller", func() {
expectedImage = fmt.Sprintf("quay.io/%s/%s", testQuayOrg, expectedImageName)
expectedRobotAccountPrefix = strings.ReplaceAll(strings.ReplaceAll(expectedImageName, "-", "_"), "/", "_")

createComponent(componentConfig{})
})

It("should provision image repository for component", func() {
assertProvisionRepository := func(updateComponentAnnotation bool) {
isCreateRepositoryInvoked := false
CreateRepositoryFunc = func(repository quay.RepositoryRequest) (*quay.Repository, error) {
defer GinkgoRecover()
Expand Down Expand Up @@ -312,12 +317,18 @@ var _ = Describe("Image repository controller", func() {
return nil
}

createImageRepository(imageRepositoryConfig{
imageRepositoryConfigObject := imageRepositoryConfig{
Labels: map[string]string{
ApplicationNameLabelName: defaultComponentApplication,
ComponentNameLabelName: defaultComponentName,
},
})
}

if updateComponentAnnotation {
imageRepositoryConfigObject.Annotations = map[string]string{updateComponentAnnotationName: "true"}
}

createImageRepository(imageRepositoryConfigObject)

pushUploadSecretKey := types.NamespacedName{Name: "upload-secret-" + resourceKey.Name + "-image-push", Namespace: resourceKey.Namespace}
pullUploadSecretKey := types.NamespacedName{Name: "upload-secret-" + resourceKey.Name + "-image-pull", Namespace: resourceKey.Namespace}
Expand All @@ -332,7 +343,16 @@ var _ = Describe("Image repository controller", func() {

waitImageRepositoryFinalizerOnImageRepository(resourceKey)

component := getComponent(componentKey)
imageRepository := getImageRepository(resourceKey)

if updateComponentAnnotation {
Expect(component.Spec.ContainerImage).To(Equal(imageRepository.Status.Image.URL))
Expect(imageRepository.Annotations).To(HaveLen(0))
} else {
Expect(component.Spec.ContainerImage).To(BeEmpty())
}

Expect(imageRepository.Spec.Image.Name).To(Equal(expectedImageName))
Expect(imageRepository.Spec.Image.Visibility).To(Equal(imagerepositoryv1alpha1.ImageVisibilityPublic))
Expect(imageRepository.OwnerReferences).To(HaveLen(1))
Expand Down Expand Up @@ -404,6 +424,23 @@ var _ = Describe("Image repository controller", func() {
Expect(err).To(Succeed())
pullRobotAccountName := imageRepository.Status.Credentials.PullRobotAccountName
Expect(string(pullUploadSecretAuthString)).To(Equal(fmt.Sprintf("%s:%s", pullRobotAccountName, pullToken)))
}

It("should provision image repository for component, without update component annotation", func() {
assertProvisionRepository(false)

DeleteRobotAccountFunc = func(organization, robotAccountName string) (bool, error) {
return true, nil
}
DeleteRepositoryFunc = func(organization, imageRepository string) (bool, error) {
return true, nil
}

deleteImageRepository(resourceKey)
})

It("should provision image repository for component, with update component annotation", func() {
assertProvisionRepository(true)
})

It("should regenerate tokens and update remote secret", func() {
Expand Down Expand Up @@ -508,9 +545,6 @@ var _ = Describe("Image repository controller", func() {
Eventually(func() bool { return isDeleteRobotAccountForPushInvoked }, timeout, interval).Should(BeTrue())
Eventually(func() bool { return isDeleteRobotAccountForPullInvoked }, timeout, interval).Should(BeTrue())
Eventually(func() bool { return isDeleteRepositoryInvoked }, timeout, interval).Should(BeTrue())

componentKey := types.NamespacedName{Name: defaultComponentName, Namespace: defaultNamespace}
deleteComponent(componentKey)
})
})

Expand Down
12 changes: 9 additions & 3 deletions controllers/suite_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type imageRepositoryConfig struct {
ImageName string
Visibility string
Labels map[string]string
Annotations map[string]string
}

func getImageRepositoryConfig(config imageRepositoryConfig) *imagerepositoryv1alpha1.ImageRepository {
Expand All @@ -75,12 +76,17 @@ func getImageRepositoryConfig(config imageRepositoryConfig) *imagerepositoryv1al
} else if config.Visibility == "public" {
visibility = "public"
}
annotations := make(map[string]string)
if config.Annotations != nil {
annotations = config.Annotations
}

return &imagerepositoryv1alpha1.ImageRepository{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
Labels: config.Labels,
Name: name,
Namespace: namespace,
Labels: config.Labels,
Annotations: annotations,
},
Spec: imagerepositoryv1alpha1.ImageRepositorySpec{
Image: imagerepositoryv1alpha1.ImageParameters{
Expand Down

0 comments on commit c5c0fae

Please sign in to comment.