github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/pkg/clients/has/components.go (about)

     1  package has
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/devfile/library/v2/pkg/util"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	appservice "github.com/redhat-appstudio/application-api/api/v1alpha1"
    13  	"github.com/redhat-appstudio/e2e-tests/pkg/clients/tekton"
    14  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    15  	"github.com/redhat-appstudio/e2e-tests/pkg/logs"
    16  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    17  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/build"
    18  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    19  	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	"k8s.io/apimachinery/pkg/labels"
    22  	"k8s.io/apimachinery/pkg/types"
    23  	"k8s.io/apimachinery/pkg/util/wait"
    24  	"k8s.io/client-go/util/retry"
    25  	"k8s.io/klog"
    26  	pointer "k8s.io/utils/ptr"
    27  	"knative.dev/pkg/apis"
    28  	rclient "sigs.k8s.io/controller-runtime/pkg/client"
    29  	"sigs.k8s.io/yaml"
    30  )
    31  
    32  const (
    33  	RequiredLabelNotFound = "cannot retrigger PipelineRun - required label %q not found"
    34  )
    35  
    36  // GetComponent return a component object from kubernetes cluster
    37  func (h *HasController) GetComponent(name string, namespace string) (*appservice.Component, error) {
    38  	component := &appservice.Component{}
    39  	if err := h.KubeRest().Get(context.Background(), types.NamespacedName{Name: name, Namespace: namespace}, component); err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	return component, nil
    44  }
    45  
    46  // GetComponentByApplicationName returns a component from kubernetes cluster given a application name.
    47  func (h *HasController) GetComponentByApplicationName(applicationName string, namespace string) (*appservice.Component, error) {
    48  	components := &appservice.ComponentList{}
    49  	opts := []rclient.ListOption{
    50  		rclient.InNamespace(namespace),
    51  	}
    52  	err := h.KubeRest().List(context.Background(), components, opts...)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	for _, component := range components.Items {
    57  		if component.Spec.Application == applicationName {
    58  			return &component, nil
    59  		}
    60  	}
    61  
    62  	return &appservice.Component{}, fmt.Errorf("no component found %s", utils.GetAdditionalInfo(applicationName, namespace))
    63  }
    64  
    65  // GetComponentPipeline returns the pipeline for a given component labels
    66  func (h *HasController) GetComponentPipelineRun(componentName string, applicationName string, namespace, sha string) (*pipeline.PipelineRun, error) {
    67  	return h.GetComponentPipelineRunWithType(componentName, applicationName, namespace, "", sha)
    68  }
    69  
    70  // GetComponentPipeline returns the pipeline for a given component labels with pipeline type within label "pipelines.appstudio.openshift.io/type" ("build", "test")
    71  func (h *HasController) GetComponentPipelineRunWithType(componentName string, applicationName string, namespace, pipelineType string, sha string) (*pipeline.PipelineRun, error) {
    72  	pipelineRunLabels := map[string]string{"appstudio.openshift.io/component": componentName, "appstudio.openshift.io/application": applicationName}
    73  	if pipelineType != "" {
    74  		pipelineRunLabels["pipelines.appstudio.openshift.io/type"] = pipelineType
    75  	}
    76  
    77  	if sha != "" {
    78  		pipelineRunLabels["pipelinesascode.tekton.dev/sha"] = sha
    79  	}
    80  
    81  	list := &pipeline.PipelineRunList{}
    82  	err := h.KubeRest().List(context.Background(), list, &rclient.ListOptions{LabelSelector: labels.SelectorFromSet(pipelineRunLabels), Namespace: namespace})
    83  
    84  	if err != nil && !k8sErrors.IsNotFound(err) {
    85  		return nil, fmt.Errorf("error listing pipelineruns in %s namespace: %v", namespace, err)
    86  	}
    87  
    88  	// If we hit any other error, while fetching pipelineRun list
    89  	if err != nil {
    90  		return nil, fmt.Errorf("error while trying to get pipelinerun list in %s namespace: %v", namespace, err)
    91  	}
    92  
    93  	if len(list.Items) > 0 {
    94  		return &list.Items[0], nil
    95  	}
    96  
    97  	return nil, fmt.Errorf("no pipelinerun found for component %s", componentName)
    98  }
    99  
   100  // GetAllPipelineRunsForApplication returns the pipelineruns for a given application in the namespace
   101  func (h *HasController) GetAllPipelineRunsForApplication(applicationName, namespace string) (*pipeline.PipelineRunList, error) {
   102  	pipelineRunLabels := map[string]string{"appstudio.openshift.io/application": applicationName}
   103  
   104  	list := &pipeline.PipelineRunList{}
   105  	err := h.KubeRest().List(context.Background(), list, &rclient.ListOptions{LabelSelector: labels.SelectorFromSet(pipelineRunLabels), Namespace: namespace})
   106  
   107  	if err != nil && !k8sErrors.IsNotFound(err) {
   108  		return nil, fmt.Errorf("error listing pipelineruns in %s namespace: %v", namespace, err)
   109  	}
   110  
   111  	if len(list.Items) > 0 {
   112  		return list, nil
   113  	}
   114  
   115  	return nil, fmt.Errorf("no pipelinerun found for application %s", applicationName)
   116  }
   117  
   118  // Set of options to retrigger pipelineRuns in CI to fight against flakynes
   119  type RetryOptions struct {
   120  	// Indicate how many times a pipelineRun should be retriggered in case of flakines
   121  	Retries int
   122  
   123  	// If is set to true the PipelineRun will be retriggered always in case if pipelinerun fail for any reason. Time to time in RHTAP CI
   124  	// we see that there are a lot of components which fail with QPS in build-container which cannot be controlled.
   125  	// By default is false will retrigger a pipelineRun only when meet CouldntGetTask or TaskRunImagePullFailed conditions
   126  	Always bool
   127  }
   128  
   129  // WaitForComponentPipelineToBeFinished waits for a given component PipelineRun to be finished
   130  // In case of hitting issues like `TaskRunImagePullFailed` or `CouldntGetTask` it will re-trigger the PLR.
   131  // Due to re-trigger mechanism this function can invalidate the related PLR object which might be used later in the test
   132  // (by deleting the original PLR and creating a new one in case the PLR fails on one of the attempts).
   133  // For that case this function gives an option to pass in a pointer to a related PLR object (`prToUpdate`) which will be updated (with a valid PLR object) before the end of this function
   134  // and the PLR object can be then used for making assertions later in the test.
   135  // If there's no intention for using the original PLR object later in the test, use `nil` instead of the pointer.
   136  func (h *HasController) WaitForComponentPipelineToBeFinished(component *appservice.Component, sha string, t *tekton.TektonController, r *RetryOptions, prToUpdate *pipeline.PipelineRun) error {
   137  	attempts := 1
   138  	app := component.Spec.Application
   139  	pr := &pipeline.PipelineRun{}
   140  
   141  	for {
   142  		err := wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 30*time.Minute, true, func(ctx context.Context) (done bool, err error) {
   143  			pr, err = h.GetComponentPipelineRun(component.GetName(), app, component.GetNamespace(), sha)
   144  
   145  			if err != nil {
   146  				GinkgoWriter.Printf("PipelineRun has not been created yet for the Component %s/%s\n", component.GetNamespace(), component.GetName())
   147  				return false, nil
   148  			}
   149  
   150  			GinkgoWriter.Printf("PipelineRun %s reason: %s\n", pr.Name, pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).GetReason())
   151  
   152  			if !pr.IsDone() {
   153  				return false, nil
   154  			}
   155  
   156  			if pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).IsTrue() {
   157  				return true, nil
   158  			}
   159  
   160  			var prLogs string
   161  			if err = t.StorePipelineRun(pr); err != nil {
   162  				GinkgoWriter.Printf("failed to store PipelineRun %s:%s: %s\n", pr.GetNamespace(), pr.GetName(), err.Error())
   163  			}
   164  			if prLogs, err = t.GetPipelineRunLogs(pr.Name, pr.Namespace); err != nil {
   165  				GinkgoWriter.Printf("failed to get logs for PipelineRun %s:%s: %s\n", pr.GetNamespace(), pr.GetName(), err.Error())
   166  			}
   167  
   168  			return false, fmt.Errorf(prLogs)
   169  		})
   170  
   171  		if err != nil {
   172  			GinkgoWriter.Printf("attempt %d/%d: PipelineRun %q failed: %+v", attempts, r.Retries+1, pr.GetName(), err)
   173  			// CouldntGetTask: Retry the PipelineRun only in case we hit the known issue https://issues.redhat.com/browse/SRVKP-2749
   174  			// TaskRunImagePullFailed: Retry in case of https://issues.redhat.com/browse/RHTAPBUGS-985 and https://github.com/tektoncd/pipeline/issues/7184
   175  			if attempts == r.Retries+1 || (!r.Always && pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).GetReason() != "CouldntGetTask" && pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).GetReason() != "TaskRunImagePullFailed") {
   176  				return err
   177  			}
   178  			if err = t.RemoveFinalizerFromPipelineRun(pr, constants.E2ETestFinalizerName); err != nil {
   179  				return fmt.Errorf("failed to remove the finalizer from pipelinerun %s:%s in order to retrigger it: %+v", pr.GetNamespace(), pr.GetName(), err)
   180  			}
   181  			if err = h.PipelineClient().TektonV1().PipelineRuns(pr.GetNamespace()).Delete(context.Background(), pr.GetName(), metav1.DeleteOptions{}); err != nil {
   182  				return fmt.Errorf("failed to delete PipelineRun %q from %q namespace with error: %v", pr.GetName(), pr.GetNamespace(), err)
   183  			}
   184  			if sha, err = h.RetriggerComponentPipelineRun(component, pr); err != nil {
   185  				return fmt.Errorf("unable to retrigger pipelinerun for component %s:%s: %+v", component.GetNamespace(), component.GetName(), err)
   186  			}
   187  			attempts++
   188  		} else {
   189  			break
   190  		}
   191  	}
   192  
   193  	// If prToUpdate variable was passed to this function, update it with the latest version of the PipelineRun object
   194  	if prToUpdate != nil {
   195  		pr.DeepCopyInto(prToUpdate)
   196  	}
   197  
   198  	return nil
   199  }
   200  
   201  // Universal method to create a component in the kubernetes clusters.
   202  func (h *HasController) CreateComponent(componentSpec appservice.ComponentSpec, namespace string, outputContainerImage string, secret string, applicationName string, skipInitialChecks bool, annotations map[string]string) (*appservice.Component, error) {
   203  	componentObject := &appservice.Component{
   204  		ObjectMeta: metav1.ObjectMeta{
   205  			// adding default label because of the BuildPipelineSelector in build test
   206  			Labels:    constants.ComponentDefaultLabel,
   207  			Name:      componentSpec.ComponentName,
   208  			Namespace: namespace,
   209  			Annotations: map[string]string{
   210  				"skip-initial-checks": strconv.FormatBool(skipInitialChecks),
   211  			},
   212  		},
   213  		Spec: componentSpec,
   214  	}
   215  	componentObject.Spec.Secret = secret
   216  	componentObject.Spec.Application = applicationName
   217  
   218  	if len(annotations) > 0 {
   219  		componentObject.Annotations = utils.MergeMaps(componentObject.Annotations, annotations)
   220  
   221  	}
   222  
   223  	if componentObject.Spec.TargetPort == 0 {
   224  		componentObject.Spec.TargetPort = 8081
   225  	}
   226  	if outputContainerImage != "" {
   227  		componentObject.Spec.ContainerImage = outputContainerImage
   228  	} else if componentObject.Annotations["image.redhat.com/generate"] == "" {
   229  		// Generate default public image repo since nothing is mentioned specifically
   230  		componentObject.Annotations = utils.MergeMaps(componentObject.Annotations, constants.ImageControllerAnnotationRequestPublicRepo)
   231  	}
   232  
   233  	ctx, cancel := context.WithTimeout(context.Background(), time.Minute*1)
   234  	defer cancel()
   235  	if err := h.KubeRest().Create(ctx, componentObject); err != nil {
   236  		return nil, err
   237  	}
   238  	if err := utils.WaitUntil(h.ComponentReady(componentObject), time.Minute*10); err != nil {
   239  		componentObject = h.refreshComponentForErrorDebug(componentObject)
   240  		return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", componentSpec.ComponentName, namespace, utils.ToPrettyJSONString(componentObject))
   241  	}
   242  
   243  	if utils.WaitUntil(h.CheckForImageAnnotation(componentObject), time.Minute*5) != nil {
   244  		componentObject = h.refreshComponentForErrorDebug(componentObject)
   245  		return nil, fmt.Errorf("timed out when waiting for image-controller annotations to be updated on component %s in namespace %s. component: %s", componentSpec.ComponentName, namespace, utils.ToPrettyJSONString(componentObject))
   246  	}
   247  	return componentObject, nil
   248  }
   249  
   250  // CreateComponentWithDockerSource creates a component based on container image source.
   251  func (h *HasController) CreateComponentWithDockerSource(applicationName, componentName, namespace, gitSourceURL, containerImageSource, outputContainerImage, secret string) (*appservice.Component, error) {
   252  	component := &appservice.Component{
   253  		ObjectMeta: metav1.ObjectMeta{
   254  			Name:      componentName,
   255  			Namespace: namespace,
   256  		},
   257  		Spec: appservice.ComponentSpec{
   258  			ComponentName: componentName,
   259  			Application:   applicationName,
   260  			Source: appservice.ComponentSource{
   261  				ComponentSourceUnion: appservice.ComponentSourceUnion{
   262  					GitSource: &appservice.GitSource{
   263  						URL:           gitSourceURL,
   264  						DockerfileURL: containerImageSource,
   265  					},
   266  				},
   267  			},
   268  			Secret:         secret,
   269  			ContainerImage: outputContainerImage,
   270  			Replicas:       pointer.To[int](1),
   271  			TargetPort:     8081,
   272  			Route:          "",
   273  		},
   274  	}
   275  	err := h.KubeRest().Create(context.Background(), component)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	return component, nil
   280  }
   281  
   282  // ScaleDeploymentReplicas scales the replicas of a given deployment
   283  func (h *HasController) ScaleComponentReplicas(component *appservice.Component, replicas *int) (*appservice.Component, error) {
   284  	component.Spec.Replicas = replicas
   285  
   286  	err := h.KubeRest().Update(context.Background(), component, &rclient.UpdateOptions{})
   287  	if err != nil {
   288  		return &appservice.Component{}, err
   289  	}
   290  	return component, nil
   291  }
   292  
   293  // DeleteComponent delete an has component from a given name and namespace
   294  func (h *HasController) DeleteComponent(name string, namespace string, reportErrorOnNotFound bool) error {
   295  	// temporary logs
   296  	start := time.Now()
   297  	GinkgoWriter.Printf("Start to delete component '%s' at %s\n", name, start.Format(time.RFC3339))
   298  
   299  	component := appservice.Component{
   300  		ObjectMeta: metav1.ObjectMeta{
   301  			Name:      name,
   302  			Namespace: namespace,
   303  		},
   304  	}
   305  	if err := h.KubeRest().Delete(context.Background(), &component); err != nil {
   306  		if !k8sErrors.IsNotFound(err) || (k8sErrors.IsNotFound(err) && reportErrorOnNotFound) {
   307  			return fmt.Errorf("error deleting a component: %+v", err)
   308  		}
   309  	}
   310  
   311  	// RHTAPBUGS-978: temporary timeout to 15min
   312  	err := utils.WaitUntil(h.ComponentDeleted(&component), 15*time.Minute)
   313  
   314  	// temporary logs
   315  	deletionTime := time.Since(start).Minutes()
   316  	GinkgoWriter.Printf("Finish to delete component '%s' at %s. It took '%f' minutes\n", name, time.Now().Format(time.RFC3339), deletionTime)
   317  
   318  	return err
   319  }
   320  
   321  // DeleteAllComponentsInASpecificNamespace removes all component CRs from a specific namespace. Useful when creating a lot of resources and want to remove all of them
   322  func (h *HasController) DeleteAllComponentsInASpecificNamespace(namespace string, timeout time.Duration) error {
   323  	// temporary logs
   324  	start := time.Now()
   325  	GinkgoWriter.Println("Start to delete all components in namespace '%s' at %s", namespace, start.String())
   326  
   327  	if err := h.KubeRest().DeleteAllOf(context.Background(), &appservice.Component{}, rclient.InNamespace(namespace)); err != nil {
   328  		return fmt.Errorf("error deleting components from the namespace %s: %+v", namespace, err)
   329  	}
   330  
   331  	componentList := &appservice.ComponentList{}
   332  
   333  	err := utils.WaitUntil(func() (done bool, err error) {
   334  		if err := h.KubeRest().List(context.Background(), componentList, &rclient.ListOptions{Namespace: namespace}); err != nil {
   335  			return false, nil
   336  		}
   337  		return len(componentList.Items) == 0, nil
   338  	}, timeout)
   339  
   340  	// temporary logs
   341  	deletionTime := time.Since(start).Minutes()
   342  	GinkgoWriter.Println("Finish to delete all components in namespace '%s' at %s. It took '%f' minutes", namespace, time.Now().Format(time.RFC3339), deletionTime)
   343  
   344  	return err
   345  }
   346  
   347  // Waits for a component to be reconciled in the application service.
   348  func (h *HasController) ComponentReady(component *appservice.Component) wait.ConditionFunc {
   349  	return func() (bool, error) {
   350  		messages, err := h.GetComponentConditionStatusMessages(component.Name, component.Namespace)
   351  		if err != nil {
   352  			return false, nil
   353  		}
   354  		for _, m := range messages {
   355  			if strings.Contains(m, "success") {
   356  				return true, nil
   357  			}
   358  		}
   359  		return false, nil
   360  	}
   361  }
   362  
   363  // Waits for a component until is deleted and if not will return an error
   364  func (h *HasController) ComponentDeleted(component *appservice.Component) wait.ConditionFunc {
   365  	return func() (bool, error) {
   366  		_, err := h.GetComponent(component.Name, component.Namespace)
   367  		return err != nil && k8sErrors.IsNotFound(err), nil
   368  	}
   369  }
   370  
   371  // Get the message from the status of a component. Usefull for debugging purposes.
   372  func (h *HasController) GetComponentConditionStatusMessages(name, namespace string) (messages []string, err error) {
   373  	c, err := h.GetComponent(name, namespace)
   374  	if err != nil {
   375  		return messages, fmt.Errorf("error getting HAS component: %v", err)
   376  	}
   377  	for _, condition := range c.Status.Conditions {
   378  		messages = append(messages, condition.Message)
   379  	}
   380  	return
   381  }
   382  
   383  // Universal method to retrigger pipelineruns in kubernetes cluster
   384  func (h *HasController) RetriggerComponentPipelineRun(component *appservice.Component, pr *pipeline.PipelineRun) (sha string, err error) {
   385  	prLabels := pr.GetLabels()
   386  	// In case of PipelineRun managed by PaC we are able to retrigger the pipeline only
   387  	// by updating the related branch
   388  	if prLabels["app.kubernetes.io/managed-by"] == "pipelinesascode.tekton.dev" {
   389  		var ok bool
   390  		var repoName, eventType, branchName string
   391  		pacRepoNameLabelName := "pipelinesascode.tekton.dev/url-repository"
   392  		pacEventTypeLabelName := "pipelinesascode.tekton.dev/event-type"
   393  		componentLabelName := "appstudio.openshift.io/component"
   394  		targetBranchAnnotationName := "build.appstudio.redhat.com/target_branch"
   395  
   396  		if repoName, ok = prLabels[pacRepoNameLabelName]; !ok {
   397  			return "", fmt.Errorf(RequiredLabelNotFound, pacRepoNameLabelName)
   398  		}
   399  		if eventType, ok = prLabels[pacEventTypeLabelName]; !ok {
   400  			return "", fmt.Errorf(RequiredLabelNotFound, pacEventTypeLabelName)
   401  		}
   402  		// PipelineRun is triggered from a pull request, need to update the PaC PR source branch
   403  		if eventType == "pull_request" {
   404  			if len(prLabels[componentLabelName]) < 1 {
   405  				return "", fmt.Errorf(RequiredLabelNotFound, componentLabelName)
   406  			}
   407  			branchName = constants.PaCPullRequestBranchPrefix + prLabels[componentLabelName]
   408  		} else {
   409  			// No straightforward way to get a target branch from PR labels -> using annotation
   410  			if branchName, ok = pr.GetAnnotations()[targetBranchAnnotationName]; !ok {
   411  				return "", fmt.Errorf("cannot retrigger PipelineRun - required annotation %q not found", targetBranchAnnotationName)
   412  			}
   413  		}
   414  		file, err := h.Github.CreateFile(repoName, util.GenerateRandomString(5), "test", branchName)
   415  		if err != nil {
   416  			return "", fmt.Errorf("failed to retrigger PipelineRun %s in %s namespace: %+v", pr.GetName(), pr.GetNamespace(), err)
   417  		}
   418  		sha = file.GetSHA()
   419  
   420  		// To retrigger simple build PipelineRun we just need to update the initial build annotation
   421  		// in Component CR
   422  	} else {
   423  		err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
   424  			component, err := h.GetComponent(component.GetName(), component.GetNamespace())
   425  			if err != nil {
   426  				return fmt.Errorf("failed to get component for PipelineRun %q in %q namespace: %+v", pr.GetName(), pr.GetNamespace(), err)
   427  			}
   428  			component.Annotations = utils.MergeMaps(component.Annotations, constants.ComponentTriggerSimpleBuildAnnotation)
   429  			if err = h.KubeRest().Update(context.Background(), component); err != nil {
   430  				return fmt.Errorf("failed to update Component %q in %q namespace", component.GetName(), component.GetNamespace())
   431  			}
   432  			return err
   433  		})
   434  
   435  		if err != nil {
   436  			return "", err
   437  		}
   438  	}
   439  	watch, err := h.PipelineClient().TektonV1().PipelineRuns(component.GetNamespace()).Watch(context.Background(), metav1.ListOptions{})
   440  	if err != nil {
   441  		return "", fmt.Errorf("error when initiating watch for new PipelineRun after retriggering it for component %s:%s", component.GetNamespace(), component.GetName())
   442  	}
   443  	newPRFound := false
   444  	for {
   445  		select {
   446  		case <-time.After(5 * time.Minute):
   447  			return "", fmt.Errorf("timed out waiting for new PipelineRun to appear after retriggering it for component %s:%s", component.GetNamespace(), component.GetName())
   448  		case event := <-watch.ResultChan():
   449  			if event.Object == nil {
   450  				continue
   451  			}
   452  			newPR, ok := event.Object.(*pipeline.PipelineRun)
   453  			if !ok {
   454  				continue
   455  			}
   456  			if pr.GetGenerateName() == newPR.GetGenerateName() && pr.GetName() != newPR.GetName() {
   457  				newPRFound = true
   458  			}
   459  		}
   460  		if newPRFound {
   461  			break
   462  		}
   463  	}
   464  
   465  	return sha, nil
   466  }
   467  
   468  // refreshComponentForErrorDebug returns the latest component object from the kubernetes cluster.
   469  func (h *HasController) refreshComponentForErrorDebug(component *appservice.Component) *appservice.Component {
   470  	retComp := &appservice.Component{}
   471  	key := rclient.ObjectKeyFromObject(component)
   472  	err := h.KubeRest().Get(context.Background(), key, retComp)
   473  	if err != nil {
   474  		//TODO let's log this somehow, but return the original component obj, as that is better than nothing
   475  		return component
   476  	}
   477  	return retComp
   478  }
   479  
   480  func (h *HasController) CheckForImageAnnotation(component *appservice.Component) wait.ConditionFunc {
   481  	return func() (bool, error) {
   482  		componentCR, err := h.GetComponent(component.Name, component.Namespace)
   483  		if err != nil {
   484  			klog.Errorf("failed to get component %s with error: %+v", component.Name, err)
   485  			return false, nil
   486  		}
   487  		annotations := componentCR.GetAnnotations()
   488  		return build.IsImageAnnotationPresent(annotations) && build.ImageRepoCreationSucceeded(annotations), nil
   489  	}
   490  }
   491  
   492  // Gets value of a specified annotation in a component
   493  func (h *HasController) GetComponentAnnotation(componentName, annotationKey, namespace string) (string, error) {
   494  	component, err := h.GetComponent(componentName, namespace)
   495  	if err != nil {
   496  		return "", fmt.Errorf("error when getting component: %+v", err)
   497  	}
   498  	return component.Annotations[annotationKey], nil
   499  }
   500  
   501  // Sets annotation in a component
   502  func (h *HasController) SetComponentAnnotation(componentName, annotationKey, annotationValue, namespace string) error {
   503  	component, err := h.GetComponent(componentName, namespace)
   504  	if err != nil {
   505  		return fmt.Errorf("error when getting component: %+v", err)
   506  	}
   507  	newAnnotations := component.GetAnnotations()
   508  	newAnnotations[annotationKey] = annotationValue
   509  	component.SetAnnotations(newAnnotations)
   510  	err = h.KubeRest().Update(context.Background(), component)
   511  	if err != nil {
   512  		return fmt.Errorf("error when updating component: %+v", err)
   513  	}
   514  	return nil
   515  }
   516  
   517  // StoreComponent stores a given Component as an artifact.
   518  func (h *HasController) StoreComponent(component *appservice.Component) error {
   519  	artifacts := make(map[string][]byte)
   520  
   521  	componentConditionStatus, err := h.GetComponentConditionStatusMessages(component.Name, component.Namespace)
   522  	if err != nil {
   523  		return err
   524  	}
   525  	artifacts["component-condition-status-"+component.Name+".log"] = []byte(strings.Join(componentConditionStatus, "\n"))
   526  
   527  	componentYaml, err := yaml.Marshal(component)
   528  	if err != nil {
   529  		return err
   530  	}
   531  	artifacts["component-"+component.Name+".yaml"] = componentYaml
   532  
   533  	if err := logs.StoreArtifacts(artifacts); err != nil {
   534  		return err
   535  	}
   536  
   537  	return nil
   538  }
   539  
   540  // StoreAllComponents stores all Components in a given namespace.
   541  func (h *HasController) StoreAllComponents(namespace string) error {
   542  	componentList := &appservice.ComponentList{}
   543  	if err := h.KubeRest().List(context.Background(), componentList, &rclient.ListOptions{Namespace: namespace}); err != nil {
   544  		return err
   545  	}
   546  
   547  	for _, component := range componentList.Items {
   548  		if err := h.StoreComponent(&component); err != nil {
   549  			return err
   550  		}
   551  	}
   552  	return nil
   553  }
   554  
   555  // specific for tests/remote-secret/image-repository-cr-image-pull-remote-secret.go
   556  func (h *HasController) CreateComponentWithoutGenerateAnnotation(componentSpec appservice.ComponentSpec, namespace string, secret string, applicationName string, skipInitialChecks bool) (*appservice.Component, error) {
   557  	componentObject := &appservice.Component{
   558  		ObjectMeta: metav1.ObjectMeta{
   559  			// adding default label because of the BuildPipelineSelector in build test
   560  			Labels:    constants.ComponentDefaultLabel,
   561  			Name:      componentSpec.ComponentName,
   562  			Namespace: namespace,
   563  			Annotations: map[string]string{
   564  				"skip-initial-checks": strconv.FormatBool(skipInitialChecks),
   565  			},
   566  		},
   567  		Spec: componentSpec,
   568  	}
   569  	componentObject.Spec.Secret = secret
   570  	componentObject.Spec.Application = applicationName
   571  
   572  	if componentObject.Spec.TargetPort == 0 {
   573  		componentObject.Spec.TargetPort = 8081
   574  	}
   575  
   576  	if err := h.KubeRest().Create(context.Background(), componentObject); err != nil {
   577  		return nil, err
   578  	}
   579  
   580  	if err := utils.WaitUntil(h.ComponentReady(componentObject), time.Minute*10); err != nil {
   581  		componentObject = h.refreshComponentForErrorDebug(componentObject)
   582  		return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", componentSpec.ComponentName, namespace, utils.ToPrettyJSONString(componentObject))
   583  	}
   584  
   585  	return componentObject, nil
   586  }